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

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

这篇文章来讲解来配置server side render,我们先从最简单的方法开始,用cli的方式模拟实现SSR。

demo在这里

主要内容

  1. 添加webpack的server render配置

  2. 使用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...

这样少编译一套代码,觉得这样维护起来更方便,但是后来实践发现有几个问题:

  1. import "xxx.styl",引入样式文件会报错。

  2. 这种模式下需要使用babel-registerbabel编译速度较慢,开发模式下每次修改文件再重启服务器耗时太长。

  3. 影响生产环境下执行效率。

最后权衡下,还是决定使用现在多一套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",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即可:

  1. React.createElement

  2. ReactDOMServer.renderToString

React.createElement

这里简单解释下,React.createElementReact类进行实例化,实例化后的组件就可以进行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沾边,因为Providerconnect(...)(Layout)functional component,本身不会多渲染出来HTML,因此可以不用Redux参与渲染。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如果组件之中有复用的代码,需要重新创建一个父类,父类中存储公共代码,返回子类,同时把公用属性...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例如我们的 setState 函数式同步执行的,我们的事件处理直接绑定在了 dom 元素上,这些都跟 re...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom 转为真实 dom 进行挂载。其实函数是组件和类组件也是在这个基础上包裹了一层,一个是调...
react 本身提供了克隆组件的方法,但是平时开发中可能很少使用,可能是不了解。我公司的项目就没有使用,但是在很多三方库中都有使用。本小节我们来学习下如果使用该...
mobx 是一个简单可扩展的状态管理库,中文官网链接。小编在接触 react 就一直使用 mobx 库,上手简单不复杂。
我们在平常的开发中不可避免的会有很多列表渲染逻辑,在 pc 端可以使用分页进行渲染数限制,在移动端可以使用下拉加载更多。但是对于大量的列表渲染,特别像有实时数据...
本小节开始前,我们先答复下一个同学的问题。上一小节发布后,有小伙伴后台来信问到:‘小编你只讲了类组件中怎么使用 ref,那在函数式组件中怎么使用呢?’。确实我们...
上一小节我们了解了固定高度的滚动列表实现,因为是固定高度所以容器总高度和每个元素的 size、offset 很容易得到,这种场景也适合我们常见的大部分场景,例如...
上一小节我们处理了 setState 的批量更新机制,但是我们有两个遗漏点,一个是源码中的 setState 可以传入函数,同时 setState 可以传入第二...
我们知道 react 进行页面渲染或者刷新的时候,会从根节点到子节点全部执行一遍,即使子组件中没有状态的改变,也会执行。这就造成了性能不必要的浪费。之前我们了解...
在平时工作中的某些场景下,你可能想在整个组件树中传递数据,但却不想手动地通过 props 属性在每一层传递属性,contextAPI 应用而生。
楼主最近入职新单位了,恰好新单位使用的技术栈是 react,因为之前一直进行的是 vue2/vue3 和小程序开发,对于这些技术栈实现机制也有一些了解,最少面试...
我们上一节了了解了函数式组件和类组件的处理方式,本质就是处理基于 babel 处理后的 type 类型,最后还是要处理虚拟 dom。本小节我们学习下组件的更新机...
前面几节我们学习了解了 react 的渲染机制和生命周期,本节我们正式进入基本面试必考的核心地带 -- diff 算法,了解如何优化和复用 dom 操作的,还有...
我们在之前已经学习过 react 生命周期,但是在 16 版本中 will 类的生命周期进行了废除,虽然依然可以用,但是需要加上 UNSAFE 开头,表示是不安...
上一小节我们学习了 react 中类组件的优化方式,对于 hooks 为主流的函数式编程,react 也提供了优化方式 memo 方法,本小节我们来了解下它的用...
开源不易,感谢你的支持,❤ star me if you like concent ^_^
hel-micro,模块联邦sdk化,免构建、热更新、工具链无关的微模块方案 ,欢迎关注与了解
本文主题围绕concent的setup和react的五把钩子来展开,既然提到了setup就离不开composition api这个关键词,准确的说setup是由...
ReactsetState的执行是异步还是同步官方文档是这么说的setState()doesnotalwaysimmediatelyupdatethecomponent.Itmaybatchordefertheupdateuntillater.Thismakesreadingthis.staterightaftercallingsetState()apotentialpitfall.Instead,usecom