为什么要做同构

要回答这个问题,首先要问什么是同构。所谓同构,顾名思义就是同一套代码,既可以运行在客户端(浏览器),又可以运行在服务器端(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组件是否满足同构要求的自动化检测等。这些事情都是后续要一步一步去做的,到时候也会做一些整理和积累。

React 同构开发(一)的更多相关文章

  1. React 同构开发(二)

    React 同构 所谓同构,简单的说就是客户端的代码可以在服务端运行,好处就是能极大的提升首屏时间,避免白屏,另外同构也给SEO提供了很多便利. React 同构得益于 React 的虚拟 DOM.虚 ...

  2. 打造高可靠与高性能的React同构解决方案

    前言 随着React的兴起, 结合Node直出的性能优势和React的组件化,React同构已然成为趋势之一.享受技术福利的同时,直面技术挑战,在复杂场景下,挑战10倍以上极致的性能优化. 什么是同构 ...

  3. React同构直出优化总结

    收录待用,修改转载已取得腾讯云授权 作者:郭林烁 joeyguo 原文地址 React 的实践从去年在 PC QQ家校群开始,由于 PC 上的网络及环境都相当好,所以在使用时可谓一帆风顺,偶尔遇到点小 ...

  4. 自制的React同构脚手架

    代码地址如下:http://www.demodashi.com/demo/12575.html Web前端世界日新月异变化太快,为了让自己跟上节奏不掉队,总结出了自己的一套React脚手架,方便日后新 ...

  5. 腾讯新闻构建高性能的 react 同构直出方案

    在腾讯新闻抢金达人活动 node 同构直出渲染方案的总结文章中我们整体了解了下同构直出渲染方案在我们项目中的使用.正如我在上篇文章结尾所说的: 应用型技术的难点不是在克服技术问题,而是在于能够不断的结 ...

  6. [webpack] 配置react+es6开发环境

    写在前面 每次开新项目都要重新安装需要的包,简单记录一下. 以下仅包含最简单的功能: 编译react 编译es6 打包src中入口文件index.js至dist webpack配置react+es6开 ...

  7. React Native开发入门

    目录: 一.前言 二.什么是React Native 三.开发环境搭建 四.预备知识 五.最简单的React Native小程序 六.总结 七.参考资料   一.前言 虽然只是简单的了解了一下Reac ...

  8. React阶段开发总结

    这次独立编写了React页面主要是数据切换.点击不同的按钮,Ajax请求不同的后台数据.数据驱动表格内容的显示.使用React组件开发. 开发中获得下面的心得: 1.后台给的地址早一点添加路由(写好数 ...

  9. React Native开发技术周报2

    (1).资讯 1.React Native 0.22_rc版本发布 添加了热自动重载功能 (2).技术文章 1.用 React Native 设计的第一个 iOS 应用 我们想为用户设计一款移动端的应 ...

随机推荐

  1. SQL server 累加求和

    1. SELECT SalesOrderID, ProductID, OrderQty    ,SUM(OrderQty) OVER(PARTITION BY SalesOrderID) AS Tot ...

  2. 简述各大 Linux 发行版,有主观,不完全,望见谅

    只罗列当前热门的linux发行版 更多关于 Linux 以及 Linux 衍生版的内容可以参阅 中文wiki Debian 系 Debian:开源社区的代表性 linux 系统,每2年一次更新,现在的 ...

  3. linux 系统启动

    系统启动流程 BIOS 我们称之为基本输入输出系统,一般保存在主板上的BIOS芯片中,负责检查硬件并且查找可启动设备:可设置启动顺序: 如果一个设备是可启动,那么第一个扇区512字节的最后两字节是55 ...

  4. HTTP 常见异常状态及Delphi IDHTTP 控件处理方式

    以下部分为网上查找,部分为工作中整理 200:请求成功 202:请求被接受,但处理尚未完成 302:请求到的资源在一个不同的URL处临时保存     处理方式:重定向到临时的URL(IDHTTP处理方 ...

  5. Android 常用第三方框架总结

    一.导航拦 1. FlycoTabLayout https://github.com/H07000223/FlycoTabLayout    2.CoordinatorTabLayout    htt ...

  6. 7z文件格式及其源码的分析(二)

    这是第二篇, 第一篇在这里: 这一篇开始分析7z的源码结构. 一. 准备工作: 1. 源码下载: 可以从官方中文主页下载:http://sparanoid.com/lab/7z/. 为了方便, 这里直 ...

  7. 布斯乘法 Mips实现 - Booth Algorithm

    看了很久网上没有现成的代码和好一点的图,因此当一回搬运工.转自stackoverflow 布斯乘法器的Mips实现方法: .data promptStart: .asciiz "This p ...

  8. HBase介绍(4)---常用shell命令

    进入hbase shell console$HBASE_HOME/bin/hbase shell如果有kerberos认证,需要事先使用相应的keytab进行一下认证(使用kinit命令),认证成功之 ...

  9. RobotFramework关键字返回参数

    业务关键字[登录]返回参数 调用时直接把return的参数值写在业务关键字的最前面,就可以使用关键字的返回值了

  10. C#应用程序所有已经打开的窗体的集合

    获取所有打开的窗体的集合 Application.OpenForms 获取其中的某个窗体 Application.OpenForms["窗体名"]