资讯专栏INFORMATION COLUMN

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

jzzlee / 2808人阅读

摘要:从零开始搭建同构应用三配置这篇文章来讲解来配置,我们先从最简单的方法开始,用的方式模拟实现。影响生产环境下执行效率。最后权衡下,还是决定使用现在多一套编译配置的方案。新建,写入以下内容以为例,注意不能少。

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

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

demo在这里

主要内容:

添加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

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(
    
        
    ,
    document.getElementById("wrap"));

而server端的编译没有和Redux沾边,因为Providerconnect(...)(Layout)functional component,本身不会多渲染出来HTML,因此可以不用Redux参与渲染。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/81385.html

相关文章

  • 从零开始搭建React同构应用(四):搭建Koa Server & 完善SSR

    摘要:从零开始搭建同构应用四搭建完善上一篇我们使用了的方式测试了,这篇文章来讲如何在前文的基础上搭建一个,实现真正意义上的。至此,一个简单的框架已经搭建完成,剩下的工作就是结合工作需要,在里面添砖加瓦啦。 从零开始搭建React同构应用(四):搭建Koa Server & 完善SSR 上一篇我们使用了CLI的方式测试了SSR,这篇文章来讲如何在前文的基础上搭建一个Koa Server,实现真...

    fizz 评论0 收藏0
  • 无痛学会各种 2 的 Vue2+Vuex2+Webpack2 前后端同构渲染

    摘要:它会检测出最大静态子树就是不需要动态性的子树并且从渲染函数中萃取出来。这样在每次重渲染的时候,它就会直接重用完全相同的同时跳过比对。需要注意的是,中的操作必须是同步的,不可以存在异步操作的情况。 新增:哈哈,最近又推出了 vue 的文章,在这里放个链接~手把手教你从零写一个简单的 VUE 感谢有人看我扯技术,这篇文章主要介绍最近非常火的vue2前端框架的特点和vue2+vuex2+we...

    fish 评论0 收藏0
  • 无痛学会各种 2 的 Vue2+Vuex2+Webpack2 前后端同构渲染

    摘要:它会检测出最大静态子树就是不需要动态性的子树并且从渲染函数中萃取出来。这样在每次重渲染的时候,它就会直接重用完全相同的同时跳过比对。需要注意的是,中的操作必须是同步的,不可以存在异步操作的情况。 新增:哈哈,最近又推出了 vue 的文章,在这里放个链接~手把手教你从零写一个简单的 VUE 感谢有人看我扯技术,这篇文章主要介绍最近非常火的vue2前端框架的特点和vue2+vuex2+we...

    30e8336b8229 评论0 收藏0
  • 无痛学会各种 2 的 Vue2+Vuex2+Webpack2 前后端同构渲染

    摘要:它会检测出最大静态子树就是不需要动态性的子树并且从渲染函数中萃取出来。这样在每次重渲染的时候,它就会直接重用完全相同的同时跳过比对。需要注意的是,中的操作必须是同步的,不可以存在异步操作的情况。 新增:哈哈,最近又推出了 vue 的文章,在这里放个链接~手把手教你从零写一个简单的 VUE 感谢有人看我扯技术,这篇文章主要介绍最近非常火的vue2前端框架的特点和vue2+vuex2+we...

    Pluser 评论0 收藏0

发表评论

0条评论

jzzlee

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<