The React.js Way系列之 通過使用 Immutable.js 的 Flux 建築結構

這篇文章是”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  


當然,它不想很困難的去寫一個有深層級對象比較的mixin,除了引用檢查,但是React.js頻繁地叫shouldComponentUpdate 方法和深層檢查的代價是昂貴的:你應該避免它發生。

我推薦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/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章