react 服务器端渲染
Server Side Rendering, also called SSR, is the ability of a JavaScript application to render on the server rather than in the browser.
服务器端渲染 (也称为SSR )是JavaScript应用程序在服务器而不是在浏览器中进行渲染的功能。
Why would we ever want to do so?
我们为什么要这样做?
Without Server Side Rendering, all your server ships is an HTML page with no body, just some script tags that are then used by the browser to render the application.
如果没有服务器端渲染,您的所有服务器都将是一个没有正文HTML页面,只有一些脚本标签,然后浏览器就会使用这些脚本标签来渲染应用程序。
Client-rendered apps are great at any subsequent user interaction after the first page load. Server Side Rendering allows us to get the sweet spot in the middle of client-rendered apps and backend-rendered apps: the page is generated server-side, but all interactions with the page once it’s been loaded are handled client-side.
客户端呈现的应用非常适合在首页加载后进行的任何后续用户交互。 服务器端渲染使我们可以在客户端渲染的应用程序和后端渲染的应用程序的中间位置获得优势:页面是在服务器端生成的,但是与页面加载后的所有交互都在客户端处理。
However Server Side Rendering has its drawback too:
但是,服务器端渲染也有其缺点:
SSR setups can grow very, very complex and most tutorials will bake in Redux, React Router and many other concepts from the start.
SSR的设置可能会变得非常非常非常复杂,并且大多数教程从一开始都会涉及Redux , React Router和许多其他概念。
To understand how SSR works, let’s start from the basics to implement a proof of concept.
为了了解SSR的工作原理,让我们从基础开始实施概念验证。
Feel free to skip this paragraph if you just want to look into the libraries that provide SSR and not bother with the ground work
如果您只想研究提供SSR的库,而不必打扰基础工作,请跳过本段
To implement basic SSR we’re going to use Express.
为了实现基本的SSR,我们将使用Express。
If you are new to Express, or need some catch-up, check out my free Express Handbook here: https://flaviocopes.com/page/ebooks/.
如果您不熟悉Express,或者需要一些帮助,请在这里查看我的免费Express Handbook: https : //flaviocopes.com/page/ebooks/ 。
Warning: the complexity of SSR can grow with the complexity of your application. This is the bare minimum setup to render a basic React app. For more complex needs you might need to do a bit more work or also check out SSR libraries for React.
警告:SSR的复杂性会随着应用程序的复杂性而增加。 这是渲染基本React应用程序的最低要求。 对于更复杂的需求,您可能需要做更多的工作,或者还检查React的SSR库。
I assume you started a React app with create-react-app
. If you are just trying, install one now using npx create-react-app ssr
.
我假设您使用create-react-app
启动了一个React create-react-app
。 如果您只是尝试,请立即使用npx create-react-app ssr
安装一个。
Go to the main app folder with the terminal, then run:
使用终端转到主应用程序文件夹,然后运行:
npm install express
You have a set of folders in your app directory. Create a new folder called server
, then go into it and create a file named server.js
.
您的应用程序目录中有一组文件夹。 创建一个名为server
的新文件夹,然后进入其中并创建一个名为server.js
的文件。
Following the create-react-app
conventions, the app lives in the src/App.js
file. We’re going to load that component, and render it to a string using ReactDOMServer.renderToString(), which is provided by react-dom
.
遵循create-react-app
约定,该应用程序位于src/App.js
文件中。 我们将加载该组件,并使用react-dom
提供的ReactDOMServer.renderToString()将其呈现为字符串。
You get the contents of the ./build/index.html
file, and replace the <div id="root"></div>
placeholder, which is the tag where the application hooks by default, with `<div id="root">\${ReactDOMServer.renderToString(<App />)}</div>
.
你得到的内容./build/index.html
文件,并替换<div id="root"></div>
占位符,其是标签,其中默认的应用钩子,与` <div id="root">\${ReactDOMServer.renderToString(<App />)}</div>
。
All the content inside the build
folder is going to be served as-is, statically by Express.
Express会按原样提供build
文件夹中的所有内容。
import path from 'path'
import fs from 'fs'
import express from 'express'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import App from '../src/App'
const PORT = 8080
const app = express()
const router = express.Router()
const serverRenderer = (req, res, next) => {
fs.readFile(path.resolve('./build/index.html'), 'utf8', (err, data) => {
if (err) {
console.error(err)
return res.status(500).send('An error occurred')
}
return res.send(
data.replace(
'<div id="root"></div>',
`<div id="root">${ReactDOMServer.renderToString(<App />)}</div>`
)
)
})
}
router.use('^/$', serverRenderer)
router.use(
express.static(path.resolve(__dirname, '..', 'build'), { maxAge: '30d' })
)
// tell the app to use the above rules
app.use(router)
// app.use(express.static('./build'))
app.listen(PORT, () => {
console.log(`SSR running on port ${PORT}`)
})
Now, in the client application, in your src/index.js
, instead of calling ReactDOM.render()
:
现在,在客户端应用程序中,在您的src/index.js
,而不是调用ReactDOM.render()
:
ReactDOM.render(<App />, document.getElementById('root'))
call ReactDOM.hydrate()
, which is the same but has the additional ability to attach event listeners to existing markup once React loads:
调用ReactDOM.hydrate()
,这是相同的,但是具有额外的功能,一旦React加载,便可以将事件侦听器附加到现有标记:
ReactDOM.hydrate(<App />, document.getElementById('root'))
All the Node.js code needs to be transpiled by Babel, as server-side Node.js code does not know anything about JSX, nor ES Modules (which we use for the include
statements).
所有的Node.js代码都需要由Babel编译,因为服务器端Node.js代码对JSX或ES模块(我们用于include
语句)一无所知。
Install these 4 packages:
安装以下4个软件包:
npm install @babel/register @babel/preset-env @babel/preset-react ignore-styles
ignore-styles
is a Babel utility that will tell it to ignore CSS files imported using the import
syntax.
ignore-styles
是Babel实用程序,它将告诉它忽略使用import
语法导入CSS文件。
Let’s create an entry point in server/index.js
:
让我们在server/index.js
创建一个入口点:
require('ignore-styles')
require('@babel/register')({
ignore: [/(node_modules)/],
presets: ['@babel/preset-env', '@babel/preset-react']
})
require('./server')
Build the React application, so that the build/ folder is populated:
编译React应用程序,以填充build /文件夹:
npm run build
and let’s run this:
让我们运行这个:
node server/index.js
I said this is a simplistic approach, and it is:
我说这是一种简单的方法,它是:
So while this is a good example of using ReactDOMServer.renderToString()
and ReactDOM.hydrate
to get this basic server-side rendering, it’s not enough for real world usage.
因此,尽管这是一个使用ReactDOMServer.renderToString()
和ReactDOM.hydrate
来获取基本服务器端渲染的很好的示例,但对于实际使用而言还不够。
SSR is hard to do right, and React has no de-facto way to implement it.
SSR很难做到正确,而且React没有实际的方法来实现它。
It’s still very much debatable if it’s worth the trouble, complication and overhead to get the benefits, rather than using a different technology to serve those pages. This discussion on Reddit has lots of opinions in that regard.
如果值得付出麻烦,复杂化和开销来获得好处,而不是使用其他技术来提供这些页面,那还是值得商de的。 关于Reddit的讨论在这方面有很多意见。
When Server Side Rendering is an important matter, my suggestion is to rely on pre-made libraries and tools that have had this goal in mind since the beginning.
当服务器端渲染很重要时,我的建议是依赖从一开始就牢记此目标的预制库和工具。
In particular, I suggest Next.js and Gatsby.
react 服务器端渲染