从零开始搭建React同构应用(三):配置SSR
这篇文章来讲解来配置server side render
,我们先从最简单的方法开始,用cli的方式模拟实现SSR。
主要内容:
添加webpack的server render配置
使用CLI的方式测试SSR输出
添加webpack的server render配置
之前我是考虑在node端直接require
源码,例如:
//hook require
require("babel-register")({
babelrc: "false",
presets: ['react'],
plugins: [
"transform-decorators-legacy",
"transform-es2015-modules-commonjs"
]
});
//直接引入源码
const IndexBundle = require("./src/index/Index.jsx");
//do server side render...
这样少编译一套代码,觉得这样维护起来更方便,但是后来实践发现有几个问题:
import "xxx.styl"
,引入样式文件会报错。这种模式下需要使用babel-register,
babel
编译速度较慢,开发模式下每次修改文件再重启服务器耗时太长。影响生产环境下执行效率。
最后权衡下,还是决定使用现在多一套ssr编译配置的方案。
在webpack.config.js添加以下代码
let serverConfig = {};
Object.assign(serverConfig, browserConfig, {
output: {
path: path.join(__dirname, 'build_server'),
filename: "[name].bundle.js",
libraryTarget: 'commonjs2' //设置导出类型,web端默认是var,node需要module.exports = xxx的形式
},
module: {
loaders: [
{
test: /\.jsx?$/,
exclude: /node_modules/,
loader: "babel-loader",
query: { //node端的babel编译配置可以简化很多
babelrc: "false",
presets: ['react'],
plugins: [
"transform-decorators-legacy",
"transform-es2015-modules-commonjs" //如果不转换成require,import 'xxx.styl'会报错
]
}
},
{
test: /\.(styl|css)$/, //node端不能 require('xx.css'),会报错
loader: 'null'
},
]
},
plugins: [
new webpack.ProvidePlugin({
React: 'react',
ReactDOM: 'react-dom',
fetch: 'isomorphic-fetch',
promise: 'promise'
}),
new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) || JSON.stringify('development')
}),
],
target: 'node',
externals: [nodeExternals()], //不把node_modules中的文件打包
});
因为serverConfig
的配置和browserConfig
相似,我就使用Object.assign
来复制一份,同时做下修改。
nodejs
启用 --harmony
参数就可以支持绝大部分的ES6,ES7语法,如async
等,因此只需要编译JSX
语法和import
语法。babel的编译速度也因此可以提高很多。babelrc: "false"
是为了屏蔽项目目录下的babel.rc
文件,那是给浏览器端编译使用的。
同时,在node环境不支持直接引入CSS文件的,如require('xx.css')
,因此在打包的时候要忽略样式文件和资源文件,否则会报错。
这里我使用了webpack-node-externals插件,这个插件的原理是利用了webapck中的externals配置项,来剔除node_modules
文件的,因为默认webapck会把所有用到的js文件统统打包,而我们由于是在node端,因此不需要把用到的库也打包了。
执行试试
npm run watch
如果不用webpack-node-externals,打包出的文件体积会大很多
测试SSR输出
其实使用React的ssr很简单,熟悉下面两个API即可:
React.createElement
这里简单解释下,React.createElement
把React类
进行实例化,实例化后的组件就可以进行mount操作了,在浏览器环境我们是使用ReactDOM.render()
来进行挂载操作的。
ReactDOMServer.renderToString
而ReactDOMServer.renderToString
则是把React实例渲染成HTML标签。
测试
这里我们先不搭建HTTP server,暂时用cli的方式模拟一下,方便大家理解。
新建cli.js,写入以下内容(以Index.jsx
为例),注意:.defalut
不能少。
/**
* Created by chenchen on 2017/2/4.
*
* React server render 命令行测试
*/
//以Index.jsx为例
const IndexBundle = require("../build_server/index.bundle.js");
const React = require("react");
const ReactDOMServer = require("react-dom/server");
let {renderToString} = ReactDOMServer;
let initialData = {todoList: ['11', '22', '33']};
let instance = React.createElement(IndexBundle.default, initialData); //.defalut不能少
let str = renderToString(instance);
console.log(str);
我们添加一条npm script
"test-ssr": "node --harmony test/cli.js"
执行后效果如图
可以看到我们已经成功输出了组件渲染后的HTML文本了。
下一篇文章我将讲解如何搭建一个简单的Koa server
,并结合这边文章内容,实现真正意义上的server side render
^_^。
要注意的地方
React生命周期
React组件的声明周期只会到componentWillMount
,因此你不能在componentWillMount
及其之前的生命周期钩子中写浏览器环境下的代码,如$.ajax(...)
,会报错。
前后端数据同步
要注意浏览器端和服务器端的数据要一致,否则会出现HTML重用失败的错误:
server side render 没有用到redux
可能有人会疑惑,在浏览器编译的代码是:
//初始数据,用于和server render数据同步
let initialData = window._SERVER_DATA || {};
let store = createStore(reducers, initialData, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
let App = connect(_ => _)(Layout);//用connect包装一下,这里只用到mapStateToProps,而且不对state加以过滤
ReactDOM.render(
<Provider store={store}>
<App/>
</Provider>,
document.getElementById('wrap'));
而server端的编译没有和Redux沾边,因为Provider
和connect(...)(Layout)
是functional component
,本身不会多渲染出来HTML,因此可以不用Redux参与渲染。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。