React 同构开发(一)

为何要作同构

要回答这个问题,首先要问什么是同构。所谓同构,顾名思义就是同一套代码,既能够运行在客户端(浏览器),又能够运行在服务器端(node)。html

咱们知道,在前端的开发过程当中,咱们通常都会有一个index.html, 在这个文件中写入页面的基本内容(静态内容),而后引入JavaScript脚本根据用户的操做更改页面的内容(数据)。在性能优化方面,一般咱们所说的种种优化措施也都是在这个基础之上进行的。在这个模式下,前端全部的工做彷佛都被限制在了这一亩三分地之上。前端

那么同构给了咱们什么样的不一样呢?前面说到,在同构模式下,客户端的代码也能够运行在服务器上。换句话说,咱们在服务器端就能够将不一样的数据组装成页面返回给客户端(浏览器)。这给页面的性能,尤为是首屏性能带来了巨大的提高可能。另外,在SEO等方面,同构也提供了极大的便利。除此之外,在整个开发过程当中,同构会极大的下降先后端的沟通成本,后端更加专一于业务模型,前端也能够专一于页面开发,中间的数据转换大能够交给node这一层来实现,省去了不少来回沟通的成本。node

基于React的同构开发

说了这么多,如何作同构开发呢?
这还得归功于 React提供的服务端渲染。react

ReactDOMServer.renderToString  
ReactDOMServer.renderToStaticMarkup

不一样于 ReactDom.render将DOM结构渲染到页面, 这两个函数将虚拟DOM在服务端渲染为一段字符串,表明了一段完整的HTML结构,最终以html的形式吐给客户端。web

下面看一个简单的例子:redux

// 定义组件 
import React, { Component, PropTypes } from 'react';

class News extends Component {
    constructor(props) {
        super(props);
    }

    render() {
        var {data} = this.props;
        return <div className="item">
      <a href={data.url}>{ data.title }</a>
    </div>;
    }
}

export default News;

咱们在客户端,一般经过以下方式渲染这个组件:后端

// 中间省略了不少其余内容,例如redux等。
let data = {url: 'http://www.taobao.com', title: 'taobao'}
ReactDom.render(<News data={data} />, document.getElementById("container"));

在这个例子中咱们写死了数据,一般状况下,咱们须要一个异步请求拉取数据,再将数据经过props传递给News组件。这时候的写法就相似于这样:浏览器

Ajax.request({params, success: function(data) {
    ReactDom.render(<News data={data} />, document.getElementById("container"));    
}});

这时候,异步的时间就是用户实际等待的时间。性能优化

那么,在同构模式下,咱们怎么作呢?服务器

// 假设咱们的web服务器使用的是KOA,而且有这样的一个controller  
function* newsListController() {
    
  const data = yield this.getNews({params});

  const data = {
    'data': data
  };
  
  this.body = ReactDOMServer.renderToString(News(data));
};

这样的话,我么在服务端就生成了页面的全部静态内容,直接的效果就是减小了由于首屏数据请求致使的用户的等待时间。除此之外,在禁用JavaScript的浏览器中,咱们也能够提供足够的数据内容了。

什么原理

其实,react同构开发并无上面的例子那么简单。上面的例子只是为了说明服务端渲染与客户端渲染的基本不一样点。其实,及时已经在服务端渲染好了页面,咱们仍是要在客户端从新使用ReactDom.render函数在render一次的。由于所谓的服务端渲染,仅仅是渲染静态的页面内容而已,并不作任何的事件绑定。全部的事件绑定都是在客户端进行的。为了不客户端重复渲染,React提供了一套checksum的机制。所谓checksum,就是React在服务端渲染的时候,会为组件生成相应的校验和(checksum),这样客户端React在处理同一个组件的时候,会复用服务端已生成的初始DOM,增量更新,这就是data-react-checksum的做用。

因此,最终,咱们的同构应该是这个样子的:

// server 端  
function* newsListController() {
    
  const data = yield this.getNews({params});

  const data = {
    'data': data
  };
  let news = ReactDOMServer.renderToString(News(data));
  this.body = '<!doctype html>\n\
                      <html>\
                        <head>\
                            <title>react server render</title>\
                        </head>\
                        <body><div id="container">' +
                            news +
                            '</div><script>var window.__INIT_DATA='+ JSON.stringify(data) +'</script><script src="app.js"></script>\
                        </body>\
                      </html>';
};

// 客户端,app.js中  
let data = JSON.parse(window.__INIT_DATA__);  
ReactDom.render(<News props={data} />, document.getElementById("container"));

小结

最近一直在作同构相关的东西,本文主要讨论react同构开发的基本原理和方式,做为一个引子,其中省去了不少细节问题。关于同构应用开发,其实有不少事情要作,好比node应用的发布、监控、日志管理,react组件是否知足同构要求的自动化检测等。这些事情都是后续要一步一步去作的,到时候也会作一些整理和积累。

相关文章
相关标签/搜索