通过服务端渲染(SSR)我们可以将应用的 HTML、JavaScript、CSS 代码,甚至是应用的初始状态数据在首次请求时就返回给浏览器。浏览器在拿到数据后,即使在 JavaScript 被禁用的状态下也能正确的展示页面。除此以外,服务端渲染还可以帮助实现搜索引擎优化(SEO)。
我们现在来展示一下如何使用 SSR。
添加 Babel
通常来说,在 React 中我们都会使用 JSX,因此,需要先配置 babel。
npm add babel-loader @babel/core @babel/preset-react --develop
.babelrc
{
"presets": [
["@babel/preset-env", { "modules": false }],
"@babel/preset-react"
]
}
2
3
4
5
6
添加 React demo
安装 react 包。
npm add react react-dom
做服务端渲染,在应用入口处有两种情况需要考虑。在浏览器端渲染时,我们需要将应用渲染到页面上。而在服务端时,我们只需要返回应用的 JSX 代码。
另外,ES2016 的导入导出语法与 CommonJS 的导入导出预发不能混用,我们需要在入口代码处统一使用 CommonJS 语法。
src/ssr.js
const React = require('react');
const ReactDOM = require('react-dom');
const SSR = <div onClick={() => alert('hello')}>Hello world</div>;
if (typeof document === 'undefined') {
module.exports = SSR;
} else {
// 浏览器端做渲染
ReactDOM.hydrate(SSR, document.getElementById('app'));
}
2
3
4
5
6
7
8
9
10
配置 Webpack
我们单独定义一份配置文件。假设我们需要在多种环境中使用相同的输出,则使用 UMD
进行输出更为合理。
webpack.ssr.js
const path = require('path');
const APP_SOURCE = path.join(__dirname, 'src');
module.exports = {
mode: 'production',
entry: { index: path.join(APP_SOURCE, 'ssr.js') },
output: {
path: path.join(__dirname, 'static'),
filename: '[name].js',
libraryTarget: 'umd',
globalObject: 'this',
},
module: {
rules: [
{
test: /\.js$/,
include: APP_SOURCE,
use: 'babel-loader',
},
],
},
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
添加构建脚本.
package.json
{
"scripts": {
"build:ssr": "wp --config webpack.ssr.js"
}
}
2
3
4
5
此时执行 npm run build:ssr
,我们可以看到新构建出来的文件 ./static/index.js
。下一步就是搭建一个服务来渲染页面。
搭建服务
我们使用 express 来举例。
npm add express --develop
server.js
const express = require('express');
const { renderToString } = require('react-dom/server');
const SSR = require('./static');
const app = express();
app.use(express.static('static'));
app.get('/', (req, res) =>
res.status(200).send(renderMarkup(renderToString(SSR)))
);
app.listen(process.env.PORT || 8080);
function renderMarkup(html) {
return `<!DOCTYPE html>
<html>
<head><title>SSR Demo</title><meta charset="utf-8" /></head>
<body>
<div id="app">${html}</div>
<script src="./index.js"></script>
</body>
</html>`;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
启动服务,node ./server.js
,访问 http://localhost:8080
就可以看到调试工具的 Source
标签页中看到服务端渲染返回的内容了。
此时如果修改了应用代码,刷新页面数据不会更新。我们可以通过 webpack-dev-middlewareopen in new window 来解决这个问题。
上面这是简单演示了一个服务端渲染的 demo。有很多实际的问题没有解决,比如如何加载一个非 JavaScript 模块,像 CSS、图片等直接在服务端加载会导致报错。社区有很多成熟的服务端渲染框架,比如 Next.js 可以帮助解决这些问题。
预渲染(Prerendering)
预渲染同样可以解决 SEO 问题。预渲染通过一个无头浏览器将页面内容渲染完成后提供给爬虫。相对于服务端渲染来说,预渲染更易实现。预渲染也有缺点,就是对频繁变化的数据支持的不够理想。
在 Webpack 中,我们可以使用这些工具来实现预渲染。
- prerender-spa-pluginopen in new window,底层使用 Puppeteeropen in new window
- prerender-loaderopen in new window 可以提供高度定制化的选项。可以与 html-webpack-plugin 结合使用。
总结
服务端渲染可以为浏览器在加载初始 JavaScript 的时候提供更多的展示内容,同时也带来了更大的技术挑战。Webpack 可以为服务端渲染提供更多的资源构建支持。在一些主流的服务端渲染框架中,比如 Next.js 中都内置了 Webpack 来构建资源。
关注微信公众号,获取最新推送~
加微信,深入交流~