服务端渲染-简介

2020-07-28 11:15:282020-10-16 17:32:50

什么是SSR

SSR(server side rendering),服务端渲染。当后端/服务器接收到网络请求后,生成(渲染)HTML字符串,将它们直接发送到浏览器

与之对立的则是CSR(client side rendering),客户端渲染。后端不提供完整的html页面,而是提供一些api使得前端可以获取json数据,然后前端拿到json数据之后再在前端进行html页面拼接,然后展示在浏览器上。这种是客户端渲染。这样前端就可以专注UI的开发,后端专注逻辑开发。典型的应用是SPA

CSRSSR最大的区别在于前者的页面渲染是JS负责进行的,而后者是服务器端直接返回HTML让浏览器直接渲染

为什么要有SSR

传统CSR的弊端不容小觑:

  • 首屏加载缓慢
  • 对于SEO无能为力

怎么做SSR

原理(基于React.js)

  1. 编写一个简单的React组件

  2. 使用react-dom/server中的renderToString编译虚拟DOM

  3. 将转换后的DOM插入html中返回给前端

    // containers/Home.js
    import React from 'react';
    const Home = () => {
      return (
        <div>
          <div>This is sanyuan</div>
        </div>
      )
    }
    export default Home
    
    // server/index.js
    import express from 'express';
    import { renderToString } from 'react-dom/server';
    import Home from './containers/Home';
    
    const app = express();
    const content = renderToString(<Home />);
    
    app.get('/', function (req, res) {
      res.send(
      `
        <html>
          <head>
            <title>ssr</title>
          </head>
          <body>
            <div id="root">${content}</div>
          </body>
        </html>
      `
      );
    })
    app.listen(3001, () => {
      console.log('listen:3001')
    })
    

此时已实现一个简单的SSR,但对于一些常见的场景如事件绑定无效,这时就需要进行了同构。所谓同构,通俗的讲,就是一套React代码在服务器上运行一遍,到达浏览器又运行一遍。服务端渲染完成页面结构,浏览器端渲染完成事件绑定。这样的话就需要浏览器去拉取额外的JS文件,由JS来完成一些复杂的事情

  1. 使用react-dom关联DOMJS

  2. 使用webpack打包JS

  3. 开启express的静态服务

    // client/index. js
    import React from 'react';
    import ReactDom from 'react-dom';
    import Home from '../containers/Home';
    
    ReactDom.hydrate(<Home />, document.getElementById('root'))
    
    // webpack.client.js
    const path = require('path');
    const merge = require('webpack-merge');
    const config = require('./webpack.base');
    
    const clientConfig = {
      mode: 'development',
      entry: './src/client/index.js',
      output: {
        filename: 'index.js',
        path: path.resolve(__dirname, 'public')
      },
    }
    
    module.exports = merge(config, clientConfig);
    
    //webpack.base.js
    module.exports = {
      module: {
        rules: [{
          test: /\.js$/,
          loader: 'babel-loader',
          exclude: /node_modules/,
          options: {
            presets: ['@babel/preset-react',  ['@babel/preset-env', {
              targets: {
                browsers: ['last 2 versions']
              }
            }]]
          }
        }]
      }
    }
    
    // package.json
    "scripts": {
      "dev": "npm-run-all --parallel dev:**",
      "dev:start": "nodemon --watch build --exec node \"./build/bundle.js\"",
      "dev:build:server": "webpack --config webpack.server.js --watch",
      "dev:build:client": "webpack --config webpack.client.js --watch"
    }
    
    // server/index.js
    const app = express();
    app.use(express.static('public'));
    

三方框架

Ref:

从头开始,彻底理解服务端渲染原理(8千字汇总长文)