使用Immutable js的一點體會

夢凡發表於2019-03-04

專案中一直在使用Immutable js,最開始使用Immutable js,我的內心是抗拒的,簡單的物件操作整得那麼複雜,有必要嗎。隨著後面開發對資料操作非常頻繁的專案後,漸漸發現了Immutable的閃光點;
現在就總結下使用Immutable時的一些體會;

一:什麼是 Immutable Data

Immutable Data 就是一旦建立,就不能再被更改的資料。對 Immutable 物件的任何修改或新增刪除操作都會返回一個新的 Immutable 物件。
Immutable 實現的原理是 Persistent Data >Structure(持久化資料結構),也就是使用舊資料建立新資料時,要保證舊資料同時可用且不變。同時為了避免 deepCopy 把所有節點都複製一遍帶來的效能損耗,Immutable 使用了 Structural Sharing(結構共享),即如果物件樹中一個節點發生變化,只修改這個節點和受它影響的父節點,其它節點則進行共享;

通過閱讀原始碼,知道,Immutable js 支援MapListSetCollectionOrderedMapStackOrderedSetRecordRangeRepeat九種資料結構;

最重要的也是經常用到的就是前三個;

Map:鍵值對集合,對應於 Object,ES6 也有專門的 Map 物件

List:有序可重複的列表,對應於 Array

Set:無序且不可重複的列表

二:為什麼要用ImmutableImmutable帶來的好處有哪些?

第一點,在沒有使用Immutable之前操作store物件型資料的時候在不想修改原資料的時候通常的做法是複製一份,在複製的資料上做更新修改操作;但是每次deep-copy都要把整個物件遞迴的複製一份,如果遇到很複雜的物件型資料時,這樣效能會很差;

而現在使用了Immutable,當我們發生一個set操作的時候,Immutable.js只修改這個節點和受它影響的父節點,其它節點則進行共享,可以大大提高效能;這裡在網上找到一張動圖:

使用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 js的一點體會

可以看到,多層次的物件轉為Immutable的Map物件時,只有第一層會被轉為Immutable物件。

想要每個每層都轉為Immutable物件,用fromJS:

let obj2 = Immutable.fromJS({
  aa: {
    `a`:1
  },
  bb: {
    `b`:2
  }
});
console.log(obj2);
複製程式碼

截圖如下:

使用Immutable js的一點體會

Immutable在處理Map物件時,會將需要轉成Map物件的普通物件的值放
在_root的entries物件下;這個普通物件的每個子項會是一個陣列,每個子項的key是陣列的第0項,value是陣列的第1項; 這樣儲存的好處是當使用 set或者get 方法設定或取物件時,就去迭代entries物件,設定或找到相應的值。

Immutable物件使用頻率最高的莫過於setIngetIn方法;
setIngetIn方法第一個引數都是需要查詢的層級路徑,路徑用陣列的格式;

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’ 物件;

使用Immutable js的一點體會

如果是”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會給你意想不到驚喜。

相關文章