基本概念
-
客户端渲染 服务器端仅返回 JSON 数据.DATA和 HTML在客户端进行渲染
-
服务器端渲染 服务器端返回HTML,DATA和 HTML在服务器端进行渲染
-
客户端渲染存在的问题 1.首屏等待时间长,用户体验差 2.页面结构为空,不利于 SEO
-
React SSR 同构 同构指的是代码复用.即实现客户端和服务器端最大程度的代码复用
实现React SSR
- 引入要渲染的 React组件
- 通过 renderToString 方法将 React 组件转换为 HTML 字符串
- 将结果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
- Node 环境不支持 ESModule 模块系统,不支持 JSX 语法.
- 项目启动命令配置
"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"
},
- 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"
]
}
}
}
]
}
};
- 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);
- 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);