这篇文章是”The React.js Way”博客系列的第二部分。如果你对基本的东西不太熟悉,那么我强烈建议你阅读第一篇文章FunctionsThe React.js Way: Getting Started Tutorial。
在前一篇文章里面,我们讨论了virtual DOM的概念和怎样用component的方式去思考。现在是时候把它们结合到应用里面去了,然后想清楚这些组件应该怎样和彼此之间通信。
组件就像函数
在单个组件里面,真正炫酷的事情是你可以把它想成一个JavaScript的函数。当你调用一个带参数的函数时,它返回了一个值。同样,一个React.js的组件也有一些类似的事情发生了:你传递属性,然后它返回了渲染好的DOM结构。如果你传递不同的值,你将会得到不同的响应。这使它们能够极大程度的复用,也能够非常便利的把它们结合到应用中去。这个想法来自于函数式编程(functional programming ),不包含在这篇文章的范围之内。如果你对这个感兴趣的话,我推荐你去阅读这篇博客Functional UI and Components as Higher Order。
自顶向下的渲染方式
是的,这非常的好,在一个应用里面,我们能够更容易的把我们的组件结合起来。但是在没有数据的情况下是讲不通的,我们上次讨论过,是用React.js,你的app的结构是有层级的,它会有一个根节点,你可以像传递一个参数一样传递数据,然后看你的app是如何通过这些组件响应的。你在顶部的时候传递这些数据,然后它们会从组件 到 组件 依次向下传递:这就叫做自顶向下的渲染方式。
我们在顶层传递这些数据是极好的,通过组件们的属性,它会逐级向下传递,但是在一个层级结构的更高级别,如果有什么东西需要发生改变,我们应该如何通知组件呢?举个例子,当用户按下了一个按钮的时候?
我们需要把一些东西把我们应用里面的真实状态储存起来,我们可以用这些东西来通知组件,是否应该改变状态。新的状态会被传递到根节点,然后自顶向下的渲染又会再一次开始,去为我们的应用生成(re-render再次渲染)新的输出(DOM)。这里就是在这幅图画上Flux应该到来的地方。
Flux建筑风格
你可能已经听说过Flux建筑风格(Flux architecture)和它的概念。所以在这篇文章,我不会再去给出一个非常详细的关于Flux的概述。早在之前我就在这篇文章里讲过了Flux inspired libraries with React。
为构建用户界面的应用架构 -Facebook flux
小贴士:Flux是一个单向的数据流概念,这里你可以有一个Store来保存应用真实的状态,就像保存真实的数据一样。当它改变之后,它可以发出事件,然后让你的应用的组件们知道哪些应该被重新渲染的。它也有一个可以控制中心的调度程序,可以在你的应用和Store之间搭建一个桥梁。你可以从你的应用叫actons,它会从Store调用程序。Store被这些事件订阅,当需要的时候,它内部的状态会发生改变。
PureRenderMixin
我们与我们当前的应用程序?我们有一个数据存储,它包含了真实的状态。我们可以和Store通信,并且传递数据到我们的应用中去,它会用渲染好的DOM来响应到来的状态。这非常棒,但是听起来有许多要渲染(的确是这样)。记住组件的层级结构和自顶向下的渲染方式-所有的东西都会为新的数据响应。
早之前我提到过,虚拟DOM很好的优化了DOM的操作,但是这并不意味着我们不应该把它的工作量减少到最小化。为了这个,基于新的和现在的属性,我们必须告诉组件是否它应该为到来的属性重新渲染。在React.js lifecycle ,你可以使用shouldCoponentUpdate这样做
React.js很幸运的拥有一个mixin,叫做PureRenderMixin,它可以把新来的属性和之前的属性比较,然后当它们相同的时候,会停止渲染。它会在内部调用shouldComponentUpdate 方法。
boolean shouldComponentUpdate(object nextProps,object nextState)
这非常的好,但是PureRenderMixin 不能比较对象属性。它检查引用相等(===),会返回false当不同的对象带有相同的数据时。
var a = { foo: 'bar' }; var b = { foo: 'bar' }; a === b; // false
当shouldComponentUpdate返回false,接着render()将会被跳过直到下一个state发生变化。(而且,componentWillUpdate 和 componentDidUpdate 将不会被叫)
var a = { foo: 'bar' }; var b = a; b.foo = 'baz'; a === b; // true
我推荐Facebook写的React.js 文章,去检验 advanced Performance (优先性能)。
不变性
如果我们的应用状态是单一的,大的,嵌套的对象,就像我们的FLux store一样,问题就很快的开始逐渐上升了。
当它没有发生改变,并且有一个新的对象的时候,我们想要保持对象引用相同。这就是Immutable.js做的事情。
一旦被创建之后,不变的数据就不能够被改变了,这样使开发应用变得更加简单,没有防御的复制,也没有授权的优先的记忆和为了简单逻辑改变技术方向的问题。
检查下面的代码片段:
var stateV1 = Immutable.fromJS({ users: [ { name: 'Foo' },{ name: 'Bar' } ] }); var stateV2 = stateV1.updateIn(['users',1],function () { return Immutable.fromJS({ name: 'Barbar' }); }); stateV1 === stateV2; // false stateV1.getIn(['users',0]) === stateV2.getIn(['users',0]); // true stateV1.getIn(['users',1]) === stateV2.getIn(['users',1]); // false
正如你看到的,我们可以使用===通过引用去比较我们的对象,这就意味着我们有一个超快的方式去进行对象比较,并且它可以和React的PureRenderMixin 并存。根据这个我们应该用Immutable.js来写我们整个的应用。我们的Flux Store应该是一个不变的对象,我们会像属性一样传递不变的数据到我们的应用中去。
现在,让我们来再次回顾之前的代码片段,想象我们的应用组件的层级结构就像这样:
你可以看到,当状态发生改变之后,只有红色的部分会被重新渲染,因为其他的部分和之前有着相同的引用。这就意味着root组件和其中一个user会被重新渲染。
通过inmmutability中,我们优化了渲染的路径,并且给我们的应用增加了动力。通过虚拟DOM,使得“React.js way”变成了一个快速闪耀的应用建筑结构。
了解更多关于inmmutable数据结构是怎样持续工作的,点击链接观看来自React.js 2015会议的对话点击打开链接。(事实证明并不是每篇文章末尾都会有广告~)
下载文章的案例,地址:
https://github.com/RisingStack/react-way-immutable-flux
原文地址:http://blog.risingstack.com/the-react-js-way-flux-architecture-with-immutable-js/