SSR的简单实现及配置

1,166 阅读1分钟

基本概念

  1. 客户端渲染 服务器端仅返回 JSON 数据.DATA和 HTML在客户端进行渲染

  2. 服务器端渲染 服务器端返回HTML,DATA和 HTML在服务器端进行渲染

  3. 客户端渲染存在的问题 1.首屏等待时间长,用户体验差 2.页面结构为空,不利于 SEO

  4. React SSR 同构 同构指的是代码复用.即实现客户端和服务器端最大程度的代码复用

实现React SSR

  1. 引入要渲染的 React组件
  2. 通过 renderToString 方法将 React 组件转换为 HTML 字符串
  3. 将结果HTML字符串相应到客户端
  • server
import app from './http';
import React from 'react'
import Home from '../share/pages/Home';
import { renderToString } from 'react-dom/server'
app.get('/', (req, res)=>{
    const content = renderToString(<Home />)
    res.send(`
            <html>
                <head>
                    <title>SSR</title>
                </head>
                <body>
                    <div id="root">${content}</div>
                    <script src="bundle.js"></script>
                </body>
            </html>
    `)
})
// http
import express from 'express';
const app = express()
// 服务器端程序实现静态资源访问功能,客户端 JavaScript 打包文件会被作为静态资源使用
app.use(express.static('public'))
app.listen(3000, ()=>{
    console.log('app is running')
})
export default app;
  • share
import React from 'react';
const Home = () => <div>Home</div>
export default Home;
  • client
import React from 'react';
import ReactDOM from 'react-dom';
import Home from '../share/pages/Home';
// 使用 hydrate 方法对组件进行渲染,为组件元素附加事件
// hydrate方法在实现渲染的时候,会复用原本已经存在的 DOM 节点,减少重新生成节点以及删除原本 DOM 节点的开销.
ReactDOM.hydrate(<Home />, document.getElementById('root'));

配置webpack

  1. Node 环境不支持 ESModule 模块系统,不支持 JSX 语法.
  2. 项目启动命令配置
  "scripts": {
    "dev": "npm-run-all --parallel dev:*",
    "dev:client-build": "webpack --config webpack.client.js --watch",
    "dev:server-run": "nodemon --watch build --exec \"node build/bundle.js\"",
    "dev:server-build": "webpack --config webpack.server.js --watch"
  },
  1. webpack.base.js
module.exports = {
  // 指定当前为开发模式
  mode: "development",
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: [
              [
                "@babel/preset-env",
                {
                  useBuiltIns: "usage"
                }
              ],
              "@babel/preset-react"
            ]
          }
        }
      }
    ]
  }
};

  1. webpack.server.js
const path = require('path');
const merge = require('webpack-merge');
const baseConfig = require('./webpack.base');
const nodeExternals = require('webpack-node-externals');
const config = {
  // 指定项目打包目标支持环境
  target: 'node',
  // 指定项目入口文件
  entry: './src/server/index.js',
  // 指定文件打包位置及文件名称
  output: {
    path: path.join(__dirname, 'build'),
    filename: 'bundle.js'
  },
  externals: [nodeExternals()]
}
module.exports = merge(baseConfig, config);
  1. webpack.client.js
const path = require('path');
const merge = require('webpack-merge');
const baseConfig = require('./webpack.base');

const config = {
  // 指定项目入口文件
  entry: './src/client/index.js',
  // 指定文件打包位置及文件名称
  output: {
    path: path.join(__dirname, 'public'),
    filename: 'bundle.js'
  }
}

module.exports = merge(baseConfig, config);