1

服务端渲染

服务端渲染(SSR)主要是为了SEO,加快首屏的加载速度等作用。利用react-dom/server提供的工具,我们很容易进行服务端渲染。

基本原理

服务端渲染的基本原理就是读取我们的模板文件,然后将其中的内容替换成我们自己的代码,然后生成一个完整的html文件返回给前端页面。

webpack配置

第一篇文章中,已经进行了基础的配置,本文是在前面的基础上来配置的。本次配置需要安装以下两个依赖

  1. express, 涉及到服务端代码,用到express包
  2. 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可能会遇到一些权限相关的问题,可能的解决方法点这里


geology
176 声望6 粉丝

自学前端