当前位置: 首页 > 后端技术 > Node.js

从零开始搭建一个React同构应用(三):配置SSR

时间:2023-04-04 00:34:11 Node.js

从零开始搭建一个React同构应用(三):配置SSR本文介绍了如何配置serversiderender。我们先从最简单的方法开始,使用cli来模拟实现SSR。demo的主要内容在这里:添加webpackserverrender配置使用CLI测试SSR输出在添加webpackserverrender配置之前,我考虑直接在node端require源码,例如://hookrequirerequire("babel-register")({babelrc:"false",presets:['react'],plugins:["transform-decorators-legacy","transform-es2015-modules-commonjs"]});//直接导入源码constIndexBundle=require("./src/index/Index.jsx");//做服务端渲染...这样就少了一套代码编译,我觉得维护起来比较方便,但是后面我在实践中发现了几个问题:import"xxx.styl",导入样式文件会报错。在这种模式下,你需要使用babel-register。babel编译速度慢。在开发模式下,每次修改文件和重启服务器的时间太长。影响生产环境中的执行效率。最后权衡了一下,还是决定用现在的多一个ssr编译配置方案。在webpack.config.js中添加如下代码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)$/,//节点端不能require('xx.css'),会报错loader:'null'},]},插件:[newwebpack.ProvidePlugin({React:'react',ReactDOM:'react-dom',fetch:'isomorphic-fetch',promise:'promise'}),newwebpack.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语法和导入语法。babel的编译速度因此可以提升很多。babelrc:"false"是屏蔽项目目录下的babel.rc文件,用于浏览器编译。同时node环境不支持直接导入css文件,如require('xx.css'),所以打包时要忽略样式文件和资源文件,否则会报错。这里我使用的是webpack-node-externals插件。这个插件的原理是利用webapck中的externals配置项去掉node_modules文件,因为默认的webapck会打包所有用到的js文件,而我们是在node端,所以不需要打包libraries用过的。执行并尝试npmrunwatch如果不使用webpack-node-externals,打包后的文件体积会大很多。测试SSR输出。其实使用React的ssr非常简单。熟悉以下两个API即可:React.createElementReactDOMServer.renderToStringReact.createElement在这里简单解释一下,React.createElement实例化了React类,实例化后的组件就可以挂载了。在浏览器环境中,我们使用ReactDOM.render()进行挂载。ReactDOMServer.renderToString和ReactDOMServer.renderToString将React实例渲染到HTML标签中。为了测试,我们先不搭建HTTP服务器,而是使用cli方式模拟一下,方便大家理解。新建一个cli.js,写入如下内容(以Index.jsx为例),注意:.defalut一定不能少。/***由chenchen创建于2017/2/4。**Reactserverrender命令行测试*///以Index.jsx为例constIndexBundle=require("../build_server/index.bundle.js");constReact=require("react");constReactDOMServer=require("react-dom/server");let{renderToString}=ReactDOMServer;letinitialData={todoList:['11','22','33']};letinstance=React.createElement(IndexBundle.default,初始数据);//.defalut不能少letstr=renderToString(instance);console.log(str);我们在执行后添加一个npmscript"test-ssr":"node--harmonytest/cli.js",如图所示,我们可以看到我们已经成功输出了组件渲染后的HTML文本。下一篇我会讲解如何搭建一个简单的Koa服务器,结合本文的内容,实现真正的服务端渲染^_^。React生命周期需要注意的事情React组件的声明周期只到componentWillMount,所以不能在componentWillMount及其之前的生命周期钩子中编写浏览器环境的代码,比如$.ajax(...),会报错。前后端数据同步,注意浏览器端和服务端数据的一致性,否则会出现HTML重用失败的错误:serversiderenderdoesnotuseredux。有些人可能会感到困惑。在浏览器中编译的代码是://初始数据,用于与服务端渲染数据同步letinitialData=window._SERVER_DATA||{};letstore=createStore(reducers,initialData,window.__REDUX_DEVTOOLS_EXTENSION__&&window.__REDUX_DEVTOOLS_EXTENSION__());letApp=connect(_=>_)(Layout);//用connect包裹起来,这里只用到了mapStateToProps,并且状态没有被过滤ReactDOM.render(,document.getElementById('wrap'));服务端的编译与Redux无关,因为Provider和connect(...)(Layout)是函数式组件,它们不会自己渲染HTML,所以不能用Redux参与渲染。