專案中一直在使用Immutable js,最開始使用Immutable js,我的內心是抗拒的,簡單的物件操作整得那麼複雜,有必要嗎。隨著後面開發對資料操作非常頻繁的專案後,漸漸發現了Immutable的閃光點;
現在就總結下使用Immutable時的一些體會;
一:什麼是 Immutable Data
Immutable Data 就是一旦建立,就不能再被更改的資料。對 Immutable 物件的任何修改或新增刪除操作都會返回一個新的 Immutable 物件。
Immutable 實現的原理是 Persistent Data >Structure(持久化資料結構),也就是使用舊資料建立新資料時,要保證舊資料同時可用且不變。同時為了避免 deepCopy 把所有節點都複製一遍帶來的效能損耗,Immutable 使用了 Structural Sharing(結構共享),即如果物件樹中一個節點發生變化,只修改這個節點和受它影響的父節點,其它節點則進行共享;
通過閱讀原始碼,知道,Immutable js 支援Map、List、Set、Collection、OrderedMap、Stack、OrderedSet、Record、Range、Repeat九種資料結構;
最重要的也是經常用到的就是前三個;
Map:鍵值對集合,對應於 Object,ES6 也有專門的 Map 物件
List:有序可重複的列表,對應於 Array
Set:無序且不可重複的列表
二:為什麼要用Immutable,Immutable帶來的好處有哪些?
第一點,在沒有使用Immutable之前操作store物件型資料的時候在不想修改原資料的時候通常的做法是複製一份,在複製的資料上做更新修改操作;但是每次deep-copy都要把整個物件遞迴的複製一份,如果遇到很複雜的物件型資料時,這樣效能會很差;
而現在使用了Immutable,當我們發生一個set操作的時候,Immutable.js只修改這個節點和受它影響的父節點,其它節點則進行共享,可以大大提高效能;這裡在網上找到一張動圖:
第二點, 在react中的shouldComponentUpdate方法中,判斷元件是否需要更新的時候;對於複雜的物件型資料,去對比兩個物件的值是否相等是很麻煩的;而Immutable提供了 is方法,去判斷兩個Immutable物件值是否相等;
第三點,由於Immutable提供了豐富的API,對於操作複雜的資料結構也變得很直觀和方便;
粗略閱讀原始碼,探究Immutable資料結構如何設計
Map物件為例子:
let obj1 = Immutable.Map({
aa: {
`a`:1
},
bb: {
`b`:2
}
});
console.log(obj1)
複製程式碼
上面的a Immutable物件列印出來是這樣的:
可以看到,多層次的物件轉為Immutable的Map物件時,只有第一層會被轉為Immutable物件。
想要每個每層都轉為Immutable物件,用fromJS:
let obj2 = Immutable.fromJS({
aa: {
`a`:1
},
bb: {
`b`:2
}
});
console.log(obj2);
複製程式碼
截圖如下:
Immutable在處理Map物件時,會將需要轉成Map物件的普通物件的值放
在_root的entries物件下;這個普通物件的每個子項會是一個陣列,每個子項的key是陣列的第0項,value是陣列的第1項; 這樣儲存的好處是當使用 set或者get 方法設定或取物件時,就去迭代entries物件,設定或找到相應的值。
Immutable物件使用頻率最高的莫過於setIn和getIn方法;
setIn和getIn方法第一個引數都是需要查詢的層級路徑,路徑用陣列的格式;
setIn有第二個引數,第二個引數是需要設定的值;
以setIn為例,如:
obj2.setIn([`aa`, `a`], 2),
複製程式碼
Immutable會去從最頂層entries物件開始迭代; 查詢最外層的entries物件,暫且管它叫entries0,先看entries0的第0項是不是“aa”; 如果entries0的第0項不是“aa”, 就會給entrie0中新增加一項;
比如:
obj2.setIn([`cc`, `c`], 3);
複製程式碼
此時entries0第0項不是‘cc’, 所以會給entries0新增 ‘cc’ 物件;
如果是”aa”, 就會再查詢entries0的第1項;entries0的第1項在這裡也是Map物件,內部也有一個entries物件,暫且叫entries1;
這裡查詢entrie1第0項是不是‘a’,發現是‘a’, 就給 這個‘a’賦值為2;如果不是“a”, 同理,會給entries1新增一項;
下面來看getIn操作
如果getIn的第一引數提供的層級路徑在Immutable物件中找不到,會返回什麼呢?
如:
let obj3 = obj2.getIn([`dd`, `d`]);
console.log(obj3) // undefined
複製程式碼
結果返回 undefined, 這個很好理解,因為在第一個引數對應的路徑下找不到想要的key,所以返回undefined;
如果此時還有第二個引數呢,結果會怎樣?
let obj3 = obj2.getIn([`dd`, `d`], 5);
console.log(obj3) // 5
複製程式碼
結果返回5;結果證明,當使用getIn時,第一個引數無效時,結果直接返回第二個引數;(getIn方法傳第二個引數沒有意義);
在最開始使用Immutable時,需要學習新的API,檢視一些文件,需要花費一些時間;
還需要適應新的物件操作方式,從傳統的obj.a, obj[a], 轉換到obj.get(`a`), obj.get([`a`, `b`, `c`]) 等等,但是真的投入的去使用後,Immutable會給你意想不到驚喜。