In this tutorial, we are going to explore how to build a server-side rendered app with Preact. preact-router will be used for routing, unistore for state management and Webpack for JS bundling. Some existing knowledge of Preact, Unistore, and Webpack might be needed.
在本教程中,我们将探索如何使用Preact构建服务器端渲染的应用程序。 preact-router将用于路由, unistore用于状态管理, Webpack用于JS捆绑。 可能需要一些有关Preact,Unistore和Webpack的现有知识。
为什么要使用服务器端渲染的应用程序? ( Why Server-Side Rendered Apps? )
We can all agree that Single Page Apps are a very popular way of building modern web applications. When it comes to SPAs, there are two ways in which you can render the content of the app to your users; Client Side Rendering and Server-Side Rendering.
我们都可以同意单页应用程序是构建现代Web应用程序的一种非常流行的方法。 对于SPA,可以通过两种方式向用户呈现应用程序的内容。 客户端渲染和服务器端渲染。
With Client Side Rendering, whenever a user opens up the app, it sends a request to load up the layout, HTML, CSS, and JavaScript. This is all fine. But in cases (all the time) which the content of the application is dependent on the completion of successfully loading the JS scripts, this can be a problem. This means users would be forced to view an empty or a preloader while waiting for the scripts to finish loading.
使用客户端渲染,每当用户打开应用程序时,它都会发送请求以加载布局,HTML,CSS和JavaScript。 一切都很好。 但是,在某些情况下(始终),应用程序的内容取决于成功加载JS脚本的完成情况,这可能是一个问题。 这意味着在等待脚本完成加载时,用户将被迫查看空白或预加载器。
Server-Side Rendering doesn't operate like that, with SSR, your initial request straight up loads the page, layout, CSS, JavaScript, and content. SSR makes sure that data is properly initialized at render time.
服务器端渲染不能像SSR这样操作,您的初始请求会直接加载页面,布局,CSS,JavaScript和内容。 SSR确保在渲染时正确初始化数据。
Another advantage Server-Side Rendering has over Client Side Rendering is SEO.
服务器端渲染相对于客户端渲染的另一个优势是SEO。
Technologies Let's go over the technologies we'll be using to build the Server-Side Rendered App;
技术让我们研究一下将用于构建服务器端渲染应用程序的技术。
Preact Preact is a 3kb alternative to React with the same API. It aims to offer a development experience similar to React, albeit with some features stripped away such as PropTypes and Children. Unistore Unistore is a 650b centralized state container with component bindings for React and Preact. It's small size means it complements Preact nicely. Preact Router Preact Router helps to manage route in Preact applications. provides a component that conditionally renders its children when the URL matches their path. Webpack Webpack is bundler that helps to bundle JavaScript files for usage in a browser. Enough said. preact-render-to-string
Preact Preact是具有相同API的React的3kb替代方案。 它旨在提供类似于React的开发体验,尽管其中剥离了一些功能(例如PropTypes和Children) 。 Unistore Unistore是一个650b集中式状态容器,具有用于React和Preact的组件绑定。 它体积小,意味着可以很好地补充Preact。 Preact路由器 Preact路由器可帮助管理Preact应用程序中的路由。 提供一个组件,当URL匹配其子路径时,该子组件有条件地呈现其子项。 Webpack Webpack是捆绑程序,可帮助捆绑JavaScript文件以在浏览器中使用。 说够了。 预先渲染到字符串
使用Preact构建SSR应用 ( Build a SSR app with Preact )
Building this app would be divided into two. We'll first build the server-side of the code which will be in Node and Express and then we'll code the Preact part of the code.
构建此应用程序将分为两部分。 我们将首先在Node和Express中构建代码的服务器端,然后对代码的Preact部分进行编码。
The idea is to create a Preact app as it were and hook it up to a Node server using the preact-render-to-string package. It allows for rendering JSX and Preact components to an HTML string which can then be used in a server. This means we'll be creating Preact components in a src folder and hook it up to the Node server file.
这个想法是照原样创建一个Preact应用程序,并使用preact-render-to-string程序包preact-render-to-string其连接到Node服务器。 它允许将JSX和Preact组件呈现为HTML字符串,然后可以在服务器中使用。 这意味着我们将在src文件夹中创建Preact组件,并将其连接到Node服务器文件。
The first thing to do would be to create the directory for the project and create the different folders that would be needed. Create a folder named preact-unistore-ssr and run the command npm init --y inside the folder. That creates a minimal package.json and an accompanying package-lock.json.
要做的第一件事是为项目创建目录并创建所需的其他文件夹。 创建一个名为preact-unistore-ssr的文件夹, npm init --y在该文件夹中运行命令npm init --y 。 这将创建一个最小的package.json和一个随附的package-lock.json 。
Next, let's install some of the tools we'll be needing for this project. Open up the package.json file and edit with the code below, then run the npm i command.
接下来,让我们安装该项目所需的一些工具。 打开package.json文件并使用以下代码进行编辑,然后运行npm i命令。
{
"name": "preact-unistore-ssr", "version": "1.0.0", "description": "", "main": "index.js", "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": {
"babel-cli": "^6.26.0", "babel-core": "^6.26.0", "babel-loader": "^7.1.2", "babel-plugin-transform-react-jsx": "^6.24.1", "babel-preset-env": "^1.6.1", "file-loader": "^1.1.11", "url-loader": "^1.0.1", "webpack": "^3.11.0", "webpack-cli": "^2.0.13" }, "dependencies": {
"express": "^4.16.2", "preact": "^8.2.6", "preact-render-to-string": "^3.7.0", "preact-router": "^2.6.0", "unistore": "^3.0.4" } }
讯享网
That will install all the packages needed for this application. In the devDependencies object, we have some babel packages that will help with transpiling ES6 code. file-loader and url-loader are Webpack plugins that help with importing files, assets, modules e.t.c.
这将安装此应用程序所需的所有软件包。 在devDependencies对象中,我们有一些babel软件包,它们将有助于转译ES6代码。 file-loader和url-loader是Webpack插件,可帮助导入文件,资产,模块等
In the dependencies object, we install packages like Express, Preact, preact-render-to-string, preact-router, and unistore.
在dependencies对象中,我们安装了Express,Preact,preact-render-to-string,preact-router和unistore之类的包。
Next, let's create a Webpack config file, create a file named webpack.config.js in the root of the project and edit it with the code below:
接下来,让我们创建一个Webpack配置文件,在项目的根目录中创建一个名为webpack.config.js文件,并使用以下代码对其进行编辑:
讯享网const path = require("path"); module.exports = {
entry: "./src/index.js", output: {
path: path.join(__dirname, "dist"), filename: "app.js" }, module: {
rules: [ {
test: /\.js$/, loader: "babel-loader", } ] } };
In the webpack config above, we define the entry point to be a src/index.js file and the output to be dist/app.js and also set the rules for using babel. The entry point file does not exist yet but that will be created later.
在上面的webpack配置中,我们将入口点定义为src/index.js文件,将输出点定义为dist/app.js ,并设置使用babel的规则。 入口点文件尚不存在,但稍后会创建。
Since we're using Babel, we'll need to create a .babelrc file in the root of the project and put in our config.
由于使用的是Babel,因此需要在项目的根目录中创建一个.babelrc文件,并将其放入配置中。
//.babelrc {
"plugins": [ ["transform-react-jsx", {
"pragma": "h" }] ], "presets": [ ["env", {
"targets": {
"node": "current", "browsers": ["last 2 versions"] } }] ] }
生成Preact应用 ( Build the Preact App )
Next up, we'll begin to create files for the Preact side of things. Create a src folder and create the following files in it.
接下来,我们将开始为事物的Preact端创建文件。 创建一个src文件夹并在其中创建以下文件。
讯享网store/store.js About.js App.js index.js router.js
Let's begin to edit the files with the necessary code. We'll start with the store.js file. This will contain the store data and actions.
让我们开始使用必要的代码编辑文件。 我们将从store.js文件开始。 这将包含商店数据和操作。
import createStore from 'unistore' export let actions = store => ({
increment(state) {
return {
count: state.count + 1 } }, decrement(state) {
return {
count: state.count - 1 } } }) export default initialState => createStore(initialState)
in the code block above, we are exporting some set of actions, which basically increments and decrements the value of the count by 1. The actions will always receive state as first parameter and any other params may come next. The createStore function which is used to initialize the store in Unistore is also exported.
在上面的代码块中,我们正在导出一些操作,这些操作基本上将count的值增加或减少1。这些操作将始终接收状态作为第一个参数,接下来可能会出现其他任何参数。 用于初始化Unistore中的商店的createStore函数也会被导出。
Next up, we'll edit the router.js file. This contains the setup for the routes we'll be using in the app.
接下来,我们将编辑router.js文件。 这包含我们将在应用程序中使用的路线的设置。
讯享网import {
h } from 'preact' import Router from 'preact-router' import {
App } from "./App"; import {
About } from "./About"; export default () => ( <Router> <App path="/" /> <About path="/about" /> </Router> )
preact-router makes it so easy to define routes. all you have to do is import the routes and make them the children of the Router component. You can then set a prop of path to each component so that preact-router knows which component to serve for a route.
preact-router使定义路由变得如此容易。 您所要做的就是导入路由,并将其设置为Router组件的子级。 然后,您可以为每个组件设置path的prop ,以使preact-router知道要为路由使用哪个组件。
There are mainly just two routes in the application, the App.js component serves as the home route and the About.js component serves as the about page.
应用程序中主要只有两条路由,其中App.js组件充当主路由,而About.js组件充当About.js页面。
Next is the About.js file and you can proceed to edit with the following:
接下来是About.js文件,您可以进行以下编辑:
import {
h } from "preact"; import {
Link } from "preact-router/match"; export const About = () => ( <div> <p>This is a Preact app being rendered on the server. It uses Unistore for state management and preact-router for routing.</p> <Link href="/">Home</Link> </div> );
It's a simple component that has a short description and a Link component that leads to the home route.
这是一个简单的组件,具有简短说明,而Link组件则指向本地路由。
Let's open up App.js which serves as the home route and edit with the necessary code.
让我们打开用作本地路由的App.js并使用必要的代码进行编辑。
讯享网import {
h } from 'preact' import {
Link } from 'preact-router' import {
connect } from 'unistore/preact' import {
actions } from './store/store' export const App = connect('count', actions)( ({
count, increment, decrement }) => ( <div class="count"> <p>{
count}</p> <button class="increment-btn" onClick={
increment}>Increment</button> <button class="decrement-btn" onClick={
decrement}>Decrement</button> <Link href="/about">About</Link> </div> ) )
So what's happening here? The connect function is imported, as well as the actions function. In the App component, the state value, count is exposed as well as the increment and decrement actions. The increment and decrement actions are both hooked up to different buttons with the onClick event handler.
那么这里发生了什么? connect功能以及actions功能均已导入。 在App组件中,将显示状态值, count以及increment和decrement操作。 increment和decrement操作都通过onClick事件处理程序连接到不同的按钮。
The index.js file is the entry point for Webpack if you remember too well, it's going to serve as the parent component for all other components in the Preact app. Open up the file and edit with the code below.
如果您记不清楚的话, index.js文件是Webpack的入口点,它将用作Preact应用程序中所有其他组件的父组件。 打开文件并使用以下代码进行编辑。
// index.js import {
h, render } from 'preact' import {
Provider } from 'unistore/preact' import Router from './router' import createStore from './store/store' const store = createStore(window.__STATE__) const app = document.getElementById('app') render( <Provider store={
store}> <Router /> </Provider>, app, app.lastChild )
In the code block above, the Provider component is imported. It's important to specify the working environment if it's preact or react. We also import the Router component from the router.js file and the createStore function is equally imported from the store.js file.
在上面的代码块中,导入了Provider组件。 重要的是要确定工作环境是预先制定还是做出React。 我们还从router.js文件导入Router组件,并且同样从store.js文件导入createStore函数。
The const store = createStore(window.__STATE__) line is used to pass initial state from the server to client since we're building a SSR app afterall.
const store = createStore(window.__STATE__)行用于将初始状态从服务器传递到客户端,因为我们毕竟是在构建SSR应用程序。
Finally, in the render function, we wrap the Router component inside the Provider component thereby making the store available to all child components.
最后,在render函数中,我们将Router组件包装在Provider组件内,从而使存储可用于所有子组件。
That's a wrap on the client side of things. We'll now move to the server-side of the app.
那是客户端的事情。 现在,我们将移至应用程序的服务器端。
构建节点服务器 ( Build the Node Server )
To start with, a server.js file will be created, this will house the Node app that will be used for the server-side rendering.
首先,将创建一个server.js文件,该文件将包含将用于服务器端渲染的Node应用程序。
讯享网// server.js const express = require("express"); const {
h } = require("preact"); const render = require("preact-render-to-string"); import {
Provider } from 'unistore/preact' const {
App } = require("./src/App"); const path = require("path"); import Router from './src/router' import createStore from './src/store/store' const app = express(); const HTMLShell = (html, state) => ` <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css"> <title> SSR Preact App </title> </head> <body> <div id="app">${
html}</div> <script>window.__STATE__=${
JSON.stringify(state).replace(/<|>/g, '')}</script> <script src="./app.js"></script> </body> </html>` app.use(express.static(path.join(__dirname, "dist"))); app.get('', (req, res) => {
const store = createStore({
count: 0, todo: [] }) let state = store.getState() let html = render( <Provider store={
store}> <Router /> </Provider> ) res.send(HTMLShell(html, state)) }) app.listen(4000);
Let's break this down into bits.
让我们将其分解为几部分。
const express = require("express"); const {
h } = require("preact"); const render = require("preact-render-to-string"); import {
Provider } from 'unistore/preact' const {
App } = require("./src/App"); const path = require("path"); import Router from './src/router' import createStore from './src/store/store' const app = express();
In the code block above, we import the packages needed for the Node server, such as express, path, we also import preact, the Provider component from unistore and most importantly the preact-render-to-string package which enables us to do server-side rendering. The routes and store is also imported from their respective files.
在上面的代码块,我们导入所需的节点服务器的软件包,如express , path ,我们也进口preact中, Provider从零部件unistore ,最重要的是preact-render-to-string包,它使我们能够做服务器侧渲染。 路线和存储也从它们各自的文件中导入。
讯享网const HTMLShell = (html, state) => ` <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css"> <title> SSR Preact App </title> </head> <body> <div id="app">${
html}</div> <script>window.__STATE__=${
JSON.stringify(state).replace(/<|>/g, '')}</script> <script src="./app.js"></script> </body> </html>`
In the code block above, we create the base HTML that will be used for the app. In the HTML code, the state is initialiazed in the script section. The HTMLShell function accepts two params, the first one is html, this will be the output gotten from the preact-render-to-string, html is then injected inside the HTML code. The second param is the state.
在上面的代码块中,我们创建将用于该应用程序的基本HTML。 在HTML代码中,状态在script部分中初始化。 HTMLShell函数接受两个参数,第一个是html ,这是从preact-render-to-string获得的输出,然后将html注入HTML代码中。 第二个参数是状态。
app.use(express.static(path.join(__dirname, "dist"))); app.get('', (req, res) => {
const store = createStore({
count: 0}) let state = store.getState() let html = render( <Provider store={
store}> <Router /> </Provider> ) res.send(HTMLShell(html, state)) }) app.listen(4000);
In the first LOC, we tell Express to use the dist when serving static files, if you remember too well, the app.js is inside the dist folder. Next, we set a route for any request that comes into the app with app.get() and this is where the work lies. This first thing to do is to initialize the store and it's state and then create a variable that holds the value of the state.
在第一个LOC中,我们告诉Express在提供静态文件时使用dist ,如果您还记得的太久的话, app.js位于dist文件夹中。 接下来,我们使用app.get()为进入应用程序的任何请求设置路由,这就是工作所在。 首先要做的是初始化存储及其状态,然后创建一个保存状态值的变量。
Then, preact-render-to-string which was imported as render is used to render the client side Preact app alongside the Router which holds the route and Provider which provides the store to every child component.
然后,使用作为render器导入的preact preact-render-to-string来渲染客户端Preact应用程序,该应用程序与保存路由的Router以及为每个子组件提供存储的Provider一起呈现。
With that done, we can finally run the app and see what it looks like, before you do that, add the code block below to the package.json file.
完成之后,我们最终可以运行该应用程序并查看其外观,然后再将下面的代码块添加到package.json文件中。
讯享网"scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "start:client": "webpack -w", "start:server": "babel-node server.js", "dev": "npm run start:client & npm run start:server" },
These are scripts that allows us to get the app up and running, run the command npm run dev in your terminal and go to http://localhost:4000. The app should be up and running and you should get a display similar to the one below.
这些脚本使我们能够启动并运行该应用程序,在您的终端中运行命令npm run dev并转到http:// localhost:4000 。 该应用程序应已启动并正在运行,并且您将得到与以下显示类似的显示。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/72130.html