http://www.cnblogs.com/bingooo/p/5724354.html
为什么要做同构
要回答这个问题,首先要问什么是同构。所谓同构,顾名思义就是同一套代码,既可以运行在客户端(浏览器),又可以运行在服务器端(node)。
我们知道,在前端的开发过程中,我们一般都会有一个index.html
,在这个文件中写入页面的基本内容(静态内容),然后引入JavaScript脚本根据用户的操作更改页面的内容(数据)。在性能优化方面,通常我们所说的种种优化措施也都是在这个基础之上进行的。在这个模式下,前端所有的工作似乎都被限制在了这一亩三分地之上。
那么同构给了我们什么样的不同呢?前面说到,在同构模式下,客户端的代码也可以运行在服务器上。换句话说,我们在服务器端就可以将不同的数据组装成页面返回给客户端(浏览器)。这给页面的性能,尤其是首屏性能带来了巨大的提升可能。另外,在SEO等方面,同构也提供了极大的便利。除此以外,在整个开发过程中,同构会极大的降低前后端的沟通成本,后端更加专注于业务模型,前端也可以专注于页面开发,中间的数据转换大可以交给node这一层来实现,省去了很多来回沟通的成本。
基于React的同构开发
说了这么多,如何做同构开发呢?
这还得归功于 React提供的服务端渲染。
ReactDOMServer.renderToString
ReactDOMServer.renderToStaticMarkup
不同于ReactDom.render
将DOM结构渲染到页面,这两个函数将虚拟DOM在服务端渲染为一段字符串,代表了一段完整的HTML结构,最终以html的形式吐给客户端。
下面看一个简单的例子:
// 定义组件
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组件是否满足同构要求的自动化检测等。这些事情都是后续要一步一步去做的,到时候也会做一些整理和积累。