深入探究CSR与SSR并实现Mini React SSR

14 天前
/ , ,
7

深入探究CSR与SSR并实现Mini React SSR

1. 使用了 `import` 语法,这是 `ES规范`,而非 Node.js 的 `CommonJS规范`。
2. 我们使用了 `React` 的 `JSX` 语法,`JavaScript` 并不认识,需要用 `Babel` 转译成这样。

修改 `package.json`:
json "scripts": { "start": "webpack --config webpack.server.js && node ./build/server.bundle.js"

} ```

此时运行 node server.js 可以成功渲染,但是只用 renderToStringReact组件 转为了 HTML字符串,并没有任何的 Hydrate(水合)事件绑定,只有静态的 HTML。


绑定事件

既然服务端只能渲染 HTML,客户端能渲染事件,那就结合一下,再实现一遍 CSR,让页面插入一个打包后的 bundle,挂载到相同的节点,让客户端将一模一样的内容重新渲染一遍,并绑定上事件(重复渲染后面解决)。

client.js

import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './pages/index';

const root = createRoot(document.getElementById('root'));

root.render(<App />);

修改 server.js

import express from 'express';
import React from 'react';
import { renderToString } from 'react-dom/server';
import App from './pages/index';

const app = express();
app.use(express.static('public'));
const content = renderToString(<App />);

app.get('/', (req, res) => res.send(`
<html>
    <head>
        <title>Tiny React SSR</title>
    </head>
    <body>
        <div id='root'>
            ${content}
        </div>
        <script src='/client.bundle.js'></script>
    </body>
</html>    
`));

app.listen(3000, () => console.log('listening on port 3000'));

打包后的客户端代码被命名为 client.bundle.js,并放入 public 目录。

webpack.client.js

const path = require('path');

module.exports = {
  mode: 'development',
  entry: './client.js',
  output: {
    filename: 'client.bundle.js',
    path: path.resolve(__dirname, 'public'),
  },
  module: {
    rules: [
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env', '@babel/preset-react'],
          },
        },
      },
    ],
  },
};

现在的流程是

先打包 客户端JS,将引用 pages/index.js 核心 React 代码的 client.js 打包到 public 下的 client.bundle.js

然后将同样引用 pages/index.js 核心 React 代码的 server.js 打包到 build 下的 server.bundle.js 中,然后 node 开启 server.bundle.js

服务端会先渲染一遍组件代码,然后输出到 HTML 中,引用 client.bundle.js,然后用 JS 再渲染一遍,并绑定上事件。

修改 package.json

"scripts": {
  "start": "webpack --config webpack.client.js && webpack --config webpack.server.js && node ./build/server.bundle.js"
}

此时已经能交互,但是有个重复渲染问题需要解决。

HydrateRoot

React 官方提供了 HydrateRoot API,允许你在先前 react-dom/server 生成的 HTML DOM节点 中展示 React组件,简单来说就是复用服务器渲染出来的 DOM 节点

修改 client.js

import React from 'react';
import { hydrateRoot } from 'react-dom/client';
import App from './pages/index';

const root = hydrateRoot(document.getElementById('root'));

root.render(<App />);

运行 npm start,实现了 SSR + Hydration 的 React 渲染。

使用社交账号登录

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...