React-不可變數據的輔助工具

不可變數據的輔助工具(ImmutabilityHelpers)

React讓你可以使用任何你想要的數據管理風格,包括數據可變風格。然而,如果你能夠在你應用中講究性能的部分使用不可變數據,就可以很方便地實現一個快速的shouldComponentUpdate()方法來顯著提升你應用的速度。

在JavaScript中處理不可變數據比在語言層面上就設計好要難,像Clojure。但是,我們提供了一個簡單的不可變輔助工具,update(),這就讓處理這種類型的數據更加簡單了,根本不會改變你數據的表示的形式。(Dealingwith immutable data in JavaScript is more difficult than in languages designedfor it, like Clojure. However, we've provided a simpleimmutability helper, update(), that makes dealing with this type of data much easier,without fundamentallychanging how your data is represented.)

主要思想(The main idea)

如果你像這樣改變數據:

myData.x.y.z= 7;
// or...
myData.a.b.push(
9);

你無法確定哪個數據改變了,因爲之前的副本被覆蓋了。相反,你需要創建一個新的myDate副本,僅僅改變需要改變的部分。然後你就能夠在shouldComponentUpdate()中使用第三方的相等判斷來比較myData的舊副本和新對象:

var newData=deepCopy(myData);
newData.x.y.z
= 7;
newData.a.b.push(
9);

不幸的是,深拷貝是很昂貴的,而且某些時候還不可能完成。你可以通過僅拷貝需要改變的對象,重用未改變的對象來緩解這個問題。不幸的是,在當今的JavaScript裏面,這會變得很笨拙:

var newData= extend(myData,{
  x
: extend(myData.x, {
    y
: extend(myData.x.y, {z:7}),
  }),
  a
: extend(myData.a, {b: myData.a.b.concat(9)})
});

雖然這能夠非常好地提升性能(因爲僅僅淺複製log n個對象,重用餘下的),但是寫起來很痛苦。看看所有的重複書寫!這不僅僅是惱人,也提供了一個巨大的出bug的區域。

update()在這種情形下提供了簡單的語法糖,使得寫這種代碼變得更加簡單。代碼變爲:

var newData=React.addons.update(myData, {
  x
: {y: {z: {$set:7}}},
  a
: {b: {$push: [9]}}
});

雖然這種語法糖需要花點精力適應(儘管這是受MongoDB's query language的啓發),但是它沒有冗餘,是靜態可分析的,並且比可變的版本少打了很多字。(Whilethe syntax takes a little getting used to (though it's inspired by MongoDB's query language) there's no redundancy, it'sstatically analyzable and it's not much more typing than the mutative version.)

$爲前綴的鍵被稱作命令。他們“改變”的數據結構被稱爲目標。( The $-prefixed keys arecalled commands. The data structure they are "mutating" iscalled the target.)

可用的命令(Available commands)

  • {$push: array} 利用push()把目標上所有的元素放進數組push() all the items in array on the target.)。
  • {$unshift: array} 利用unshift()把目標上所有的元素放進數組unshift() all the items in array on the target.)。
  • {$splice: array of arrays} 依次使用 arrays 中的元素( array )作爲參數,在目標對象( target )上調用 splice() 方法( Array.prototype.splice.apply(target, array) )。
  • {$set: any} 整體替換目標(replace the target entirely.)。
  • {$merge: object} 合併目標和object的鍵。
  • {$apply: function} 傳入當前的值到函數,然後用新返回的值更新它(passes in the current value to the function and updates it with the new returned value.)。

示例

簡單的入棧

var initialArray= [1,2, 3];
varnewArray=update(initialArray, {$push: [4]});// => [1, 2,3, 4]

initialArray仍然是[1, 2, 3]

嵌入的集合

var collection= [1,2, {a: [12,17, 15]}];
varnewCollection= update(collection, {2: {a: {$splice: [[1,1, 13,14]]}}});
// => [1, 2, {a: [12, 13, 14, 15]}]

獲取collection中索引是2的對象,然後取得該對象鍵爲a的值,刪掉索引從1開始的一個元素(即移除17),插入1314。(This accesses collection's index 2, key a, and does a spliceof one item starting from index 1 (to remove 17) whileinserting 13and 14.)

根據現有的值更新

var obj= {a:5, b:3};
varnewObj =update(obj, {b: {$apply:function(x) {return x* 2;}}});
// => {a: 5, b: 6}
// This is equivalent, but gets verbose fordeeply nested collections:
varnewObj2 =update(obj, {b: {$set: obj.b* 2}});

(淺)合併

var obj= {a:5, b:3};
varnewObj =update(obj, {$merge: {b:6, c:7}}); // => {a: 5,b: 6, c: 7}

發佈了44 篇原創文章 · 獲贊 5 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章