在前后分离架构以前,所有的 Html 业务都是后端渲染,返回前前端显示,后端渲染把前后端逻辑耦合在一起,增大系统的复杂度,不易于扩展。React 中的 Server组件,准确的说是服务器进行渲染,无论是什么框架,最后都是生成Dom Tree 进行页面展示,React 渲染中的 Commit 阶段就是进行 Dom Tree 的更新。那么我们为什么需要服务器渲染,服务渲染主要是 SEO 友好,每个页面都可以进行 SEO 优化,React 服务端渲染,我们可以利用 React 强大组件能力进行页面的开发,从而减少了于服务代码的耦合,本文将讲述在原生 React 下实现服务器渲染。
安装依赖
初始化客户端项目并安装 Express 服务端依赖。
yarn install express
代码实现
React 中服务器的渲染不只是服务器处理,客户端也需要处理,服务器生成 Html,客户端进行浏览器中的后续处理。那么为什么服务器不能全部处理?原因不难理解,由于 React 的处理方式和原生的 JS 是有很大区别,React 了很多封装,服务器只需生成 Html,客户端进行事件绑定,如果服务出现任何异常客户端会再次渲染做兜底处理。
服务器代码
创建 server 目录,并在目录中创建文件
- index.js
// server/index.js
require("ignore-styles");
require("@babel/register")({
ignore: [/(node_modules)/],
presets: ["@babel/preset-env", "@babel/preset-react"],
});
require("./server");
- render.js
// server/render.js
import React from "react";
import { renderToPipeableStream } from "react-dom/server";
import { ServerApp } from "../src/ServerApp.js";
import manifest from "../build/asset-manifest.json"
export const
render = (response) => {
const stream = renderToPipeableStream(<ServerApp />, {
bootstrapScripts: [manifest.files['main.js']],
onShellReady() {
response.setHeader("content-type", "text/html");
stream.pipe(response);
},
onError(error) {
console.error(error);
}
});
};
- server.js
//server/server.js
const express = require("express");
const app = express();
const { render } = require("./render");
const path = require('path');
app.use('/static', express.static(
path.resolve(__dirname, '..', 'build/static'),
{ maxAge: '30d' },
));
app.get("/", (req, res) => {
render(res);
});
app.listen(3005, () => {
console.log("listening on port 3005 " + path.resolve(__dirname, '..', 'build'));
});
组件
创建一个简单的组件,服务器端渲染不要做特别处理
import React from "react";
export function ServerApp(){
const handler = ()=>{ console.log("sdfasdf")}
return (
<html>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>My app</title>
</head>
<body>
<h1>test</h1>
<div><button onClick={handler}>OK</button></div>
</body>
</html>
)
}
客户端
需要将客户端入口进行修改:
import {hydrateRoot} from 'react-dom/client';
import {ServerApp} from './ServerApp';
hydrateRoot(document, <ServerApp/>);
编译并运行
编译运行
npm run build
node server/index.js
总结
React 服务器渲染功能强大,将客户端服务的进行整合,通过 hydrateRoot 进行关联,把展示和事件处理进行了分离,在架构设计上给系统带来更灵活的处理方式,完全可以替代模板引擎。