安装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文件运行在浏览器中。
优势:前端负责页面的渲染,前端向后端发送ajax请求,后端返回为json数据,前端继续渲染页面。前端只负责渲染,后端向前端提供接口,这样前后端分离,可以给开发效率带来巨大的提升。 劣势: 客户端渲染首屏加载速度慢;服务器端渲染有利于SEO,因为爬虫只认识HTML的内容,而不认识js的内容
在node这种代码体系下遵循的是common.js的规范。所以导入导出用一下形式 const React = require('react');
module.exports = { default: Home }此时的代码也是运行不了的,因为要有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端,因此不需要把用到的库也打包了。
renderToString(),把页面转换成字符串返回给浏览器
虚拟DOM是真是DOM的一个JavaScript对象映射 客户端渲染react代码在浏览器上执行,消耗的是用户浏览器的性能。 服务端渲染,react代码服务器上执行,消耗的是服务器端的性能,带来的问题是极大的消耗了服务器端的性能。
"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 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下
在第一步中服务器值渲染出HTML,并不会渲染出事件,所以需要和浏览器段进行重构