ssr服务器端渲染

xiaoxiao2025-07-06  10

准备工作

安装node 新建一个server文件夹,用命令行在server文件夹中npm init,出现一个package.json文件,使文件夹变成一个node的包,这样的包时比较容易管理的。 然后在这个包中安装npm install express --save,此时server文件夹中多了一个node_modules文件夹。 在server文件夹中新建一个app.js 在express.js官网上,入门中选择hello world,将实例复制到app.js中,了解一下express框架.

const express = require('express') const app = express() app.get('/', (req, res) => res.send('Hello World!')) app.listen(3000, () => console.log('Example app listening on port 3000!'))

服务器段渲染指的是页面上的内容在服务器端已经生成好了,服务器把内容给到浏览器,浏览器负责渲染。 客户端渲染,是由js文件渲染出来的,js文件运行在浏览器中。

React 客户端渲染的优势和弊端

优势:前端负责页面的渲染,前端向后端发送ajax请求,后端返回为json数据,前端继续渲染页面。前端只负责渲染,后端向前端提供接口,这样前后端分离,可以给开发效率带来巨大的提升。 劣势: 客户端渲染首屏加载速度慢;服务器端渲染有利于SEO,因为爬虫只认识HTML的内容,而不认识js的内容

在服务器端编写React组件

客户端渲染流程:

浏览器发送请求服务器返回HTML浏览器发送bundle.js服务器返回bundle.js浏览器执行bundle.js中的React代码

服务器端端渲染流程:

浏览器发送请求服务器运行React代码生成页面服务器返回页面

服务器端端渲染

在node这种代码体系下遵循的是common.js的规范。所以导入导出用一下形式 const React = require('react');

module.exports = { default: Home }

此时的代码也是运行不了的,因为要有webpack配置才可以

服务器端webpack配置

在server目录下新建一个webpack.server.js文件,因为是在node端,所以必须加一项target,为了告诉webpack打包的是服务器端的文件。

const Path = require('path'); const nodeExternals = require('webpack-node-externals'); module.exports = { target: 'node', mode: 'development', entry: './src/index.js', output: { filename: 'bundle.js', path: Path.resolve(__dirname, 'build') }, externals: [nodeExternals()], module: { rules: [{ test: /\.js?$/, loader: 'babel-loader', exclude: /node_modules/, options: { presets: ['react', 'stage-0', ['env', { targets: { browsers: ['last 2 versions'] } }]] } }] } }

出现以下警告: 需要安装npm install webpack-node-externals --save externals: [nodeExternals()]

这个插件的原理是利用了webapck中的externals配置项,来剔除node_modules文件的,因为默认webapck会把所有用到的js文件统统打包,而我们由于是在node端,因此不需要把用到的库也打包了。

服务器端组件渲染

import express from 'express'; import Home from './containers/Home'; import { renderToString } from 'react-dom/server'; import React from 'react'; const app = express(); app.get('/', (req, res) => res.send(renderToString(<Home />))) app.listen(3000, () => console.log('Example app listening on port 3000!'))

renderToString(),把页面转换成字符串返回给浏览器

建立在虚拟DOM上的服务器端渲染

虚拟DOM是真是DOM的一个JavaScript对象映射 客户端渲染react代码在浏览器上执行,消耗的是用户浏览器的性能。 服务端渲染,react代码服务器上执行,消耗的是服务器端的性能,带来的问题是极大的消耗了服务器端的性能。

webpack自动打包和服务器自动重启

"build": "webpack --config webpack.server.js --watch"

–watch可以监听入口有文件及入口文件的依赖是否有变化,有变化就自动打包

npm install nodemon -g

全局安装nodemon,帮助node实现文件的监听

"start": "nodemon --watch build --exec node \"./build/bundle.js\""",

监听build文件夹是否有变化,有变化就重新运行bundle.js

总结以上就是build命令来监听文件的变化,start命令来监听build文件夹的变化

使用npm run all提升开发效率

npm install npm-run all -g

"scripts": { "dev": "npm-run-all --parallel dev:**", "dev:start": "nodemon --watch build --exec node \"./build/bundle.js\"", "dev:build": "webpack --config webpack.server.js --watch" },

npm-run-all --parallel dev:**并行执行以dev开头的所有的命令

什么叫同构

一套react代码在服务器端执行一次,在客户端再执行一次。 我们在程序中添加一个button发,并在button上添加一个事件,查看源代码的时候发现事件并没有出现。这时候我们应该用同构来实现。

在根路径下创建一个平public文件,并在其中创建一个index.js文件

import express from 'express'; import Home from './containers/Home'; import { renderToString } from 'react-dom/server'; import React from 'react'; const app = express(); app.use(express.static('public')) const content = renderToString(<Home />); app.get('/', (req, res) => { res.send( `<html> <head> <title>ssr</title> </head> <body> ${content} <script src='/index.js'></script> </body> </html>` ) }) app.listen(3000, () => console.log('Example app listening on port 3000!'))

app.use(express.static('public'));的意思是服务器发现请求的是静态文件,就到根路径下的public路径中去找。

在src下新建一个client文件夹,并在其中新建一个index.js文件,内容为一下

import React from 'react'; import ReactDom from 'react-dom'; import Home from '../containers/Home'; ReactDom.render(<Home />, document.getElementById('root'));

然后再根目录下新建一个public文件夹,在根目录下新建一个webpack.client.js文件,将上面的index.js文件打包到public文件夹中。 但在浏览器上会出现警告: react-dom.development.js:517 Warning: render(): Target node has markup rendered by React, but there are unrelated nodes as well. This is most commonly caused by white-space inserted around server-rendered markup. 这是由于同构引起的,可以将ReactDom.render换成ReactDom.hydrate来解决

如果出现以下错误: react-dom.development.js:523 Warning: Did not expect server HTML to contain the text node " " in <div>. 讲的是服务器端渲染不要出现文本节点 将

app.get('/', (req, res) => { res.send( `<html> <head> <title>ssr</title> </head> <body> <div id='root'> ${content} </div> <script src='/index.js'></script> </body> </html>` ) })

改为

app.get('/', (req, res) => { res.send( `<html> <head> <title>ssr</title> </head> <body> <div id='root'>${content}</div> <script src='/index.js'></script> </body> </html>` ) })

工程优化整理

我们发现webpack.client.js和webpack.server.js有很多相同的部分,这样会造成冗余,这时候就引入一个模块 npm install webpack-merge --save 这个工具可以帮助我们合并webpack的配置项 在根目录下新建一个webpack.base.js文件,将webpack.client.js和webpack.server.js相同的部分提取到webpack.base.js中

module.exports = { module: { rules: [{ test: /\.js?$/, loader: 'babel-loader', exclude: /node_modules/, options: { presets: ['react', 'stage-0', ['env', { targets: { browsers: ['last 2 versions'] } }]] } }] } }

webpack.client.js变成

const Path = require('path'); const merge = require('webpack-merge'); const config = require('./webpack.base.js'); const clientConfig = { mode: 'development', entry: './src/client/index.js', output: { filename: 'index.js', path: Path.resolve(__dirname, 'public') } }; module.exports = merge(config, clientConfig);

webpack.server.js变成

const Path = require('path'); const nodeExternals = require('webpack-node-externals'); const merge = require('webpack-merge'); const config = require('./webpack.base.js'); const serverConfig = { target: 'node', mode: 'development', entry: './src/index.js', output: { filename: 'bundle.js', path: Path.resolve(__dirname, 'build') }, externals: [nodeExternals()] }; module.exports = merge(config, serverConfig);

还可以对代码结构进行一些优化,服务器端的代码在src/server下,客户端代码在src/client下

阶段性总结

服务器端运行React代码渲染出HTML发送HTML给浏览器浏览器接收到内容展示浏览器加载js文件js中的React代码在浏览器端执行js中的React代码接管页面操作

在第一步中服务器值渲染出HTML,并不会渲染出事件,所以需要和浏览器段进行重构

转载请注明原文地址: https://www.6miu.com/read-5032645.html

最新回复(0)