React SSR 原理与项目工程化

客户端渲染和服务端渲染


客户端渲染
顾名思义即 React 代码在客户端渲染执行,当项目启动首次请求服务端时服务端仅返回一个 HTML 页面骨架,也就是我们所看到的项目入口文件( index.html ), 客户端收到后再根据其中的 JS 文件进行整个应用页的渲染;

服务端渲染
即 React 代码在服务端上运行,直接生成带有数据的 HTML 页面( ajax 请求均在服务器上完成 ),然后直接将该页面返回给客户端,客户端只需解析 HTML 就能展示页面。

两种渲染方式的利弊


客户端渲染

  • 优点:
    1. 前后端分离。前端专注于页面开发,后端专注于 API 开发,且前端有更多的选择性,而不需要遵循后端特定的模板。
    2. 前端代码容易维护,降低于服务器的耦合度,减少服务器端负载,降低服务器响应流。
  • 缺点:
    1. 首屏渲染速度慢,向服务器请求完 HTML 骨架后由浏览器下载 JS 文件并进行 React代码解析生成 DOM 片段拼装到 HTML 骨架中。
    2. 不利于 SEO,搜索引擎爬虫爬的是服务端中生成的页面内容。

服务端渲染( 后端 )

  • 优点
    1. 更好的 SEO,因为在后端有完整的 HTML 页面,所以爬虫更容易爬取关键信息。
    2. 首屏渲染速度快,用户体验更好。
    3. 无需占用客户端资源,即解析模板的工作完全交由后端来做,客户端只要解析标准的 HTML 页面即可,这样对于客户端的资源占用更少,尤其是移动端,也可以更省电。
    4. 后端生成生成缓存片段,这样就可以减少数据库查询浪费的时间了,且对于数据变化不大的页面非常高效 。
  • 缺点
    1. 不利于前后端分离,开发效率低。使用服务器端渲染,则无法进行分工合作,则对于前端复杂度高的项目,不利于项目高效开发。
    2. 服务器压力变大,因为 React 代码由服务端执行并生成完成页面,当外部访问量增多,可能会出现页面加载变慢( 请求阻塞 )等情况,此时可以通过负载均衡策略解决。

React + Express 构建服务端渲染案例


导读:

凡涉及到项目工程化,Webpack 配置肯定跑不掉( 所以建议学习一下Webpack相关基础 ),本案例需要安装npm-run-all 和 nodemon 全局模块,生产环境依赖如下:

案例构建思路:

Tips: server.js 代表 server目录下 index.js,client.js 代表 client 目录下 index.js,整体目录结构:

  1. 创建服务端入口文件 server.js 并利用 express 的实例创建服务器
  2. 创建客户端入口文件 client.js 并引入一些简单的 React 组件
    注意:组件渲染时如果同构则需使用ReactDom.hydrate(),否则可以使用ReactDom.render();
  3. 创建配置文件 webpack.server.js 对服务端入口文件进行打包
const path = require('path');
const config = require('./webpack.base.js');
const merge = require('webpack-merge');
const nodeExternals = require('webpack-node-externals');

const serverConfig =  {
    target: 'node',
    mode: 'development',
    entry: './src/server/index.js',
    externals: [nodeExternals()],
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'build')
    }
};

module.exports = merge(config, serverConfig);
  1. 创建配置文件 webpack.client.js 对客户端入口文件进行打包
const path = require('path');
const config = require('./webpack.base.js');
const merge = require('webpack-merge');

const clientConfig = {
    mode: 'development',
    entry: './src/client/index.js',
    output: {
        filename: 'index.js',
        path: path.resolve(__dirname, 'public')
    }
};

module.exports = merge(config, clientConfig);
  1. 创建配置文件 webpack.base.js 对公共代码抽离
module.exports = {
    module: {
        rules: [{
            test: /\.js?$/,
            loader: 'babel-loader',
            exclude: /node_modules/,
            options: {
                presets: ['@babel/react', ['@babel/env', {
                    targets: {
                        browsers: ['last 2 versions']
                    }
                }]]
            }
        }]
    }
}
  1. 在 package.json 文件中配置热更新,即一旦代码有变,则重新打包编译展示到页面
  "scripts": {
    "dev": "npm-run-all --parallel dev:**",
    "dev:start": "nodemon \"./build/bundle.js\"",
    "dev:build:server": "webpack --config webpack.server.js --watch",
    "dev:build:client": "webpack --config webpack.client.js --watch"
  }
  1. 在 server.js 引入 client.js 并利用 renderToString()进行 SSR,至此 React 代码确实进行了服务端渲染,页面也展示出来了,但是其中是存在很大问题的,那么是什么问题呢?这里大家可以去学习一下 同构 的相关知识,没有同构的 SSR 只是简单的进行 HTML 代码片段的生成而其中绑定的事件只有在浏览器端执行才会生效

总结


  • 什么项目才适合 SSR
    1. 项目要求SEO,SSR 就很合适 ( 关于 SEO,预渲染也能做到 )
    2. 需求项目某页面首屏时间要求很快,SSR 可以减少白屏时间
    3. 首屏页数据请求多

你可能感兴趣的:(React SSR 原理与项目工程化)