服务端渲染
服务端渲染(SSR)主要是为了 SEO,加快首屏的加载速度等作用。利用 react-dom/server 提供的工具,我们很容易进行服务端渲染。
基本原理
服务端渲染的基本原理就是读取我们的模板文件,然后将其中的内容替换成我们自己的代码,然后生成一个完整的 html 文件返回给前端页面。
webpack 配置
在第一篇文章中,已经进行了基础的配置,本文是在前面的基础上来配置的。本次配置需要安装以下两个依赖
express, 涉及到服务端代码,用到 express 包
rimraf, 看着名字就知道是删库跑路的包。每次我们运行 build 命令时,都会生成新的文件。我们可以用这个包先删除 dist 目录,然后在重新生成新的 dist 目录。
首先在 client 目录下新增 template.html 和 server-entry.js 两个文件。前面的 html 时模板文件,后面的 js 作为服务端的入口文件。
// template 文件很简单,只有一个 id 为 app 的 div,后面我们将会把 <!– <app /> –> 替换为我们自己的内容。
<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”UTF-8″>
<meta name=”viewport” content=”width=device-width, initial-scale=1.0″>
<meta http-equiv=”X-UA-Compatible” content=”ie=edge”>
<title>Document</title>
</head>
<body>
<div id=”app”><!– <app /> –></div>
</body>
</html>
// 入口文件目前也很简单,只是导入 App 组件
import React from ‘react’
import App from ‘./App.jsx’
export default <App />
在 build 目录下新增 webpack.config.server.js 文件,作为服务端打包的配置文件。同时为了区分客户端,将客户端的配置文件改为 webpack.config.client.js。服务端与客户端的配置基本一样,主要时入口文件和出口文件的配置不同。
entry: {
app: resolvePath(‘../client/server-entry.js’) // 服务端入口文件
},
output: {
filename: ‘server-entry.js’, // 输出文件名
path: resolvePath(‘../dist’), // 输出路劲
publicPath: ”, //
libraryTarget: ‘commonjs2’ // 模块化的方式
},
在项目目录下新建 server 文件夹,新建一个 server.js 文件,该文件主要为服务端逻辑。
const express = require(‘express’)
const ReactSSR = require(‘react-dom/server’)
const fs = require(‘fs’)
const path = require(‘path’)
const serverEntry = require(‘../dist/server-entry’).default // 打包好的服务端文件
const app =express()
const template = fs.readFileSync(path.join(__dirname, ‘../dist/index.html’), ‘utf-8’) // 读取模板文件
app.get(‘*’, function(req, res) {
const appString = ReactSSR.renderToString(serverEntry)
res.send(template.replace(‘<!– <app /> –>’, appString)) // 将模板文件中的注释替换为我们自己的内容,然后返回到客户端
})
app.listen(3333, function() {
console.log(‘server is listen on 3333’)
})
服务端渲染的基本逻辑就已经完成。接下来我们在 package.json 文件中新增一些命令。
“scripts”: {
“build:client”: “webpack –config build/webpack.config.client.js”, // 编译客户端代码
“build:server”: “webpack –config build/webpack.config.server.js”,
// 编译服务端代码
“clear”: “rimraf dist”, // 每次 build 前,先自动删除 dist 目录
“build”: “npm run clear && npm run build:client && npm run build:server”,// build 客户端和服务端的代码
“start”: “node server/server.js” // 启动服务器
},
先运行 build 命令,然后运行 start 命令,访问 localhost:3333, 就可以看到内容了。而且在 network 窗口中可以看到返回的时完整的 html 页面,而不是一个空页面。
但时目前请求 js 文件,返回的也是 html 文件。因为在 server.js 中,任意的请求都是返回 html 文件。可以通过 express 来配置静态文件目录。
// 以 /public 开头的请求都会去 dist 目录中找。
app.use(‘/public’, express.static(path.join(__dirname, ‘../dist’)))
同时需要修改客户端和服务端的 webpack 配置。
// 会在路径前加上 /public 前缀
output: {
publicPath: ‘/public’,
},
重新运行 build 和 start 命令,访问 3333 端口,就会返现请求都是正常的。从返回的 html 文件中,script 标签的 src 属性中的路径会带有 /public 前缀,这就是 publicPath 属性的作用。
至此,服务端渲染的基础配置就已经完成。本次的代码位于仓库的 2 - 5 分支。
在使用 rimraf 时,window 可能会遇到一些权限相关的问题,可能的解决方法点这里