array(2) { ["docs"]=> array(10) { [0]=> array(10) { ["id"]=> string(3) "428" ["text"]=> string(77) "Visual Studio 2017 单独启动MSDN帮助(Microsoft Help Viewer)的方法" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(8) "DonetRen" ["tagsname"]=> string(55) "Visual Studio 2017|MSDN帮助|C#程序|.NET|Help Viewer" ["tagsid"]=> string(23) "[401,402,403,"300",404]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400964" ["_id"]=> string(3) "428" } [1]=> array(10) { ["id"]=> string(3) "427" ["text"]=> string(42) "npm -v;报错 cannot find module "wrapp"" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "zzty" ["tagsname"]=> string(50) "node.js|npm|cannot find module "wrapp“|node" ["tagsid"]=> string(19) "[398,"239",399,400]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400760" ["_id"]=> string(3) "427" } [2]=> array(10) { ["id"]=> string(3) "426" ["text"]=> string(54) "说说css中pt、px、em、rem都扮演了什么角色" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(12) "zhengqiaoyin" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400640" ["_id"]=> string(3) "426" } [3]=> array(10) { ["id"]=> string(3) "425" ["text"]=> string(83) "深入学习JS执行--创建执行上下文(变量对象,作用域链,this)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "Ry-yuan" ["tagsname"]=> string(33) "Javascript|Javascript执行过程" ["tagsid"]=> string(13) "["169","191"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511399901" ["_id"]=> string(3) "425" } [4]=> array(10) { ["id"]=> string(3) "424" ["text"]=> string(30) "C# 排序技术研究与对比" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "vveiliang" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(8) ".Net Dev" ["catesid"]=> string(5) "[199]" ["createtime"]=> string(10) "1511399150" ["_id"]=> string(3) "424" } [5]=> array(10) { ["id"]=> string(3) "423" ["text"]=> string(72) "【算法】小白的算法笔记:快速排序算法的编码和优化" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "penghuwan" ["tagsname"]=> string(6) "算法" ["tagsid"]=> string(7) "["344"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511398109" ["_id"]=> string(3) "423" } [6]=> array(10) { ["id"]=> string(3) "422" ["text"]=> string(64) "JavaScript数据可视化编程学习(二)Flotr2,雷达图" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "chengxs" ["tagsname"]=> string(28) "数据可视化|前端学习" ["tagsid"]=> string(9) "[396,397]" ["catesname"]=> string(18) "前端基本知识" ["catesid"]=> string(5) "[198]" ["createtime"]=> string(10) "1511397800" ["_id"]=> string(3) "422" } [7]=> array(10) { ["id"]=> string(3) "421" ["text"]=> string(36) "C#表达式目录树(Expression)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "wwym" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(4) ".NET" ["catesid"]=> string(7) "["119"]" ["createtime"]=> string(10) "1511397474" ["_id"]=> string(3) "421" } [8]=> array(10) { ["id"]=> string(3) "420" ["text"]=> string(47) "数据结构 队列_队列实例:事件处理" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "idreamo" ["tagsname"]=> string(40) "C语言|数据结构|队列|事件处理" ["tagsid"]=> string(23) "["246","247","248",395]" ["catesname"]=> string(12) "数据结构" ["catesid"]=> string(7) "["133"]" ["createtime"]=> string(10) "1511397279" ["_id"]=> string(3) "420" } [9]=> array(10) { ["id"]=> string(3) "419" ["text"]=> string(47) "久等了,博客园官方Android客户端发布" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(3) "cmt" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511396549" ["_id"]=> string(3) "419" } } ["count"]=> int(200) } 222 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,并不会渲染出事件,所以需要和浏览器段进行重构

相关文章: