这篇文章来说解来配置server side render
,咱们先从最简单的方法开始,用cli的方式模拟实现SSR。css
demo在这里html
主要内容:node
添加webpack的server render配置react
使用CLI的方式测试SSR输出webpack
以前我是考虑在node端直接require
源码,例如:git
//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...
这样少编译一套代码,以为这样维护起来更方便,可是后来实践发现有几个问题:github
import "xxx.styl"
,引入样式文件会报错。web
这种模式下须要使用babel-register,babel
编译速度较慢,开发模式下每次修改文件再重启服务器耗时太长。ajax
影响生产环境下执行效率。npm
最后权衡下,仍是决定使用如今多一套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,打包出的文件体积会大不少
其实使用React的ssr很简单,熟悉下面两个API便可:
这里简单解释下,React.createElement
把React类
进行实例化,实例化后的组件就能够进行mount操做了,在浏览器环境咱们是使用ReactDOM.render()
来进行挂载操做的。
而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组件的声明周期只会到componentWillMount
,所以你不能在componentWillMount
及其以前的生命周期钩子中写浏览器环境下的代码,如$.ajax(...)
,会报错。
要注意浏览器端和服务器端的数据要一致,不然会出现HTML重用失败的错误:
可能有人会疑惑,在浏览器编译的代码是:
//初始数据,用于和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参与渲染。