不可變數(shù)據(jù)的輔助工具(Immutability Helpers)

2019-08-14 14:29 更新

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

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

主要思想(The main idea)

如果你像這樣改變數(shù)據(jù):

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

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

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

不幸的是,深拷貝是很昂貴的,而且某些時候還不可能完成。你可以通過僅拷貝需要改變的對象,重用未改變的對象來緩解這個問題。不幸的是,在當(dāng)今的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)})
});

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

update()在這種情形下提供了簡單的語法糖,使得寫這種代碼變得更加簡單。代碼變?yōu)椋?/p>

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

雖然這種語法糖需要花點精力適應(yīng)(盡管這是受MongoDB's query language的啟發(fā)),但是它沒有冗余,是靜態(tài)可分析的,并且比可變的版本少打了很多字。(While the syntax takes a little getting used to (though it's inspired by MongoDB's query language) there's no redundancy, it's statically analyzable and it's not much more typing than the mutative version.)

$為前綴的鍵被稱作命令。他們“改變”的數(shù)據(jù)結(jié)構(gòu)被稱為目標(biāo)。( The $-prefixed keys are called commands. The data structure they are "mutating" is called the target.)

可用的命令(Available commands)

  • {$push: array} 利用push()把目標(biāo)上所有的元素放進數(shù)組push() all the items in array on the target.)。

  • {$unshift: array} 利用unshift()把目標(biāo)上所有的元素放進數(shù)組unshift() all the items in array on the target.)。

  • {$splice: array of arrays} 對于array中的每一個元素,用元素提供的參數(shù)在目標(biāo)上調(diào)用splice()(for each item in arrays call splice() on the target with the parameters provided by the item.)。

  • {$set: any} 整體替換目標(biāo)(replace the target entirely.)。

  • {$merge: object} 合并目標(biāo)和object的鍵。

  • {$apply: function} 傳入當(dāng)前的值到函數(shù),然后用新返回的值更新它(passes in the current value to the function and updates it with the new returned value.)。

示例

簡單的入棧

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

initialArray仍然是[1, 2, 3]

嵌入的集合

var collection = [1, 2, {a: [12, 17, 15]}];var newCollection = 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 splice of one item starting from index 1 (to remove 17) while inserting 13 and 14.)

根據(jù)現(xiàn)有的值更新

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

(淺)合并

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


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號