ssr服务器端渲染

准备工作

安装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,并不会渲染出事件,所以需要和浏览器段进行重构

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值