要回答这个问题,首先要问什么是同构。所谓同构,顾名思义就是同一套代码,既能够运行在客户端(浏览器),又能够运行在服务器端(node)。html
咱们知道,在前端的开发过程当中,咱们通常都会有一个index.html
, 在这个文件中写入页面的基本内容(静态内容),而后引入JavaScript脚本根据用户的操做更改页面的内容(数据)。在性能优化方面,一般咱们所说的种种优化措施也都是在这个基础之上进行的。在这个模式下,前端全部的工做彷佛都被限制在了这一亩三分地之上。前端
那么同构给了咱们什么样的不一样呢?前面说到,在同构模式下,客户端的代码也能够运行在服务器上。换句话说,咱们在服务器端就能够将不一样的数据组装成页面返回给客户端(浏览器)。这给页面的性能,尤为是首屏性能带来了巨大的提高可能。另外,在SEO等方面,同构也提供了极大的便利。除此之外,在整个开发过程当中,同构会极大的下降先后端的沟通成本,后端更加专一于业务模型,前端也能够专一于页面开发,中间的数据转换大能够交给node这一层来实现,省去了不少来回沟通的成本。node
说了这么多,如何作同构开发呢?
这还得归功于 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组件是否知足同构要求的自动化检测等。这些事情都是后续要一步一步去作的,到时候也会作一些整理和积累。