服务端渲染
服务端渲染(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可能会遇到一些权限相关的问题,可能的解决方法点这里