两种架构
现在使用React的开发模式主要有两种——freeMarker+React以及纯静态React页面开发。本文着重介绍纯静态React页面的开发方式。
freeMarker+React
由于以前是用YUI+freeMarker进行开发,为了保证以前的页面都能够正常访问,当重构老页面时会使用这种开发方式。
在这种开发模式下由java利用freeMarker生成并Render为html,通过browserify将js打包至资源目录并在browser中加载,React将app render至div中。
React纯静态页面
利用browserify使用同构的方式进行开发,直接产出html以及js文件放置到资源文件中通过文件路径访问页面。采用这种方式开发有以下优点:
- PreRender产出的静态资源文件加载速度快
- 前后端只通过ajax进行交互,使得前后端分离,各自约定好接口之后就能进行开发。
- 同构的开发模式使得功能模块可以复用,比如模板、node的一些常用模块等等。
需要注意代码能同时在browser与node环境下执行,否则会出问题。当使用bom对象时,在componentDidMount生命周期中运行,此时node环境下已经完成了first render。
构建方式
在node环境下通过React.renderToString
方法生成html,通过这种方式生成的标签会带有data-reactid属性,储存server render结果的校验值。
当在browser中React.render
时会检查校验值是否一致,保证node以及browser环境下render的结果一致。因此开发过程中一定要保证render的结果保持一致,如果需要在browser中插入dom节点,可以使用insert等操作。禁止state以及props在两个环境下值不同。
如果通过校验,则React不会重新生成dom,只将事件监听器挂载在对应的节点下。
应用架构
采用flux的思想来组织应用,具体的方案我推荐facebook的flux或者reflux,这也是现在Github中获星最多的flux实现方案。两者的主要区别是reflux不通过Dispatcher来控制action的分发,reflux中使用了较多的magic来使得代码更加简洁高效。
如果项目的复杂程度不高(没有多个互相关联的store),我推荐使用Reflux,一般情况下其实一个store就够了,而且避免了处理store之间的通信问题。
╔═════════╗ ╔════════╗ ╔═════════════════╗
║ Actions ║──────>║ Stores ║──────>║ View Components ║
╚═════════╝ ╚════════╝ ╚═════════════════╝
^ │
└──────────────────────────────────────┘
若项目较为庞大,考虑到代码的可控性、直观,以及更好地去控制各store之间的响应逻辑,使用flux更合适。
优点
采用flux来构建应用有以下优势:
- 将state在store中统一进行管理,实现业务与组建的分离,代码结构更加清晰。
- 由于action在store中进行监听,因此事件不需要再一层层通过props来进行传递,简化代码,而且也更容易将应用拆分成更细粒度的模块。
- 尽量使用props的情况下,代码可预测性很强。
组件开发
- react认为组件就是一个状态集,尽可能使得组件只拥有props。
- 当组件需要有自己的处理逻辑时需要用到state,比如控制input的value,弹出层自动隐藏、显示的逻辑等等。
- state并不会随着porps的更新而改变,因此在使用 state 时一定要注意是否有 componentWillReceiveProps。
- 业务代码为了方便以及速度可以不写 PropTypes,但是可复用的组件使用 PropTypes 来保证组件的正常运行是必要的,组件中的工具方法可以抽取出来写测试用例。
setState
state为key-value的集合,一般来说value都是基本类型,当state的数据结构层次很深的时候,操作state就会变成很头疼的事情。
深拷贝
// shallow copy
var state = deepCopy(this.state);
state.valueWantChange = vale;
this.setState(state);
深拷贝方法没有问题,但由于deepCopy效率很低,一般都不推荐使用。
forceUpdate
this.state.valueWantChange = vale;
this.forceUpdate(); // this.setState(this.state);
在以下两种情况会用到 forceUpdate
- 手动更改了 state 之后需要触发 render
- 做了除更改props和state之外的操作后,需要render。
但是使用forceUpdate 会跳过 shouldComponentUpdate 的过程,会触发子组件的所有lifeCycle方法(包括shouldComponentUpdate)从而造成性能的浪费。因此为了组件更加清晰高效,应该避免使用forceUpdate。
Immutability Helpers
我推荐使用React.addons来管理state
。
You can alleviate this by only copying objects that need to be changed and by reusing the objects that haven’t changed.
import react from 'react/addons'
var newData = React.addons.update(myData, {
x: {y: {z: {$set: 7}}},
a: {b: {$push: [9]}}
});
this.setState(newData);
下面是update的基本语法。如果用过mongo应该对此十分熟悉。
-
{$push: array}
push() all the items in array on the target. -
{$unshift: array}
unshift() all the items in array on the target. -
{$splice: array of arrays}
for each item in arrays call splice() on the target with the parameters provided by the item. -
{$set: any}
replace the target entirely. -
{$merge: object}
merge the keys of object with the target. -
{$apply: function}
passes in the current value to the function and updates it with the new returned value.