ImmutableJS 入門教學(xué)

ImmutableJS

前言

一般來說在 JavaScript 中有兩種資料類型:Primitive(String、Number、Boolean、null、undefinded)和 Object(Reference)。在 JavaScript 中物件的操作比起 Java 容易很多,但也因?yàn)橄鄬椥圆粐?yán)謹(jǐn),所以產(chǎn)生了一些問題。在 JavaScript 中的 Object(物件)資料是 Mutable(可以變的),由于是使用 Reference 的方式,所以當(dāng)修改到複製的值也會修改到原始值。例如下面的 map2 值是指到 map1,所以當(dāng) map1 值一改,map2 的值也會受影響。

var map1 = { a: 1 }; 
var map2 = map1; 
map2.a = 2

通常一般作法是使用 deepCopy 來避免修改,但這樣作法會產(chǎn)生較多的資源浪費(fèi)。為了很好的解決這個問題,我們可以使用 Immutable Data,所謂的 Immutable Data 就是一旦建立,就不能再被修改的數(shù)據(jù)資料。

為了解決這個問題,在 2013 年時 Facebook 工程師 Lee Byron 打造了 ImmutableJS,但并沒有被預(yù)設(shè)放到 React 工具包中(雖然有提供簡化的 Helper),但 ImmutableJS 的出現(xiàn)確實(shí)解決了 React 甚至 Redux 所遇到的一些問題。

以下范例即是引入了 ImmutableJS 的效果,讀者可以發(fā)現(xiàn),雖然我們操作了 map1 的值,但會發(fā)現(xiàn)原本的 map1 并未受到影響(因?yàn)槿魏涡薷亩疾粫绊懙皆假Y料),雖然使用 deepCopy 也可以模擬類似的效果但會浪費(fèi)過多的計(jì)算資源和記憶體,ImmutableJS 則可以容易地共享沒有被修該到的資料(例如下面的資料 b 即為 map1map2 共享),因而有更好的效能表現(xiàn)。

import Immutable from 'immutable';

var map1 = Immutable.Map({ a: 1, b: 3 });
var map2 = map1.set('a', 2);

map1.get('a'); // 1
map2.get('a'); // 2

ImmutableJS 特性介紹

ImmutableJS 提供了 7 種不可修改的資料類型:List、Map、Stack、OrderedMap、Set、OrderedSetRecord。若是對 Immutable 物件操作都會回傳一個新值。其中比較常用的有 List、MapSet

  1. Map:類似于 key/value 的 object,在 ES6 也有原生 Map 對應(yīng)
const Map= Immutable.Map;

// 1. Map 大小
const map1 = Map({ a: 1 });
map1.size
// => 1

// 2. 新增或取代 Map 元素
// set(key: K, value: V)
const map2 = map1.set('a', 7);
// => Map { "a": 7 }

// 3. 刪除元素
// delete(key: K)
const map3 = map1.delete('a');
// => Map {}

// 4. 清除 Map 內(nèi)容
const map4 = map1.clear();
// => Map {}

// 5. 更新 Map 元素
// update(updater: (value: Map<K, V>) => Map<K, V>)
// update(key: K, updater: (value: V) => V)
// update(key: K, notSetValue: V, updater: (value: V) => V)
const map5 = map1.update('a', () => (7))
// => Map { "a": 7 }

// 6. 合併 Map 
const map6 = Map({ b: 3 });
map1.merge(map6);
// => Map { "a": 1, "b": 3 }
  1. List:有序且可以重複值,對應(yīng)于一般的 Array
const List= Immutable.List;

// 1. 取得 List 長度
const arr1 = List([1, 2, 3]);
arr1.size
// => 3

// 2. 新增或取代 List 元素內(nèi)容
// set(index: number, value: T)
// 將 index 位置的元素替換
const arr2 = arr1.set(-1, 7);
// => [1, 2, 7]
const arr3 = arr1.set(4, 0);
// => [1, 2, 3, undefined, 0]

// 3. 刪除 List 元素
// delete(index: number)
// 刪除 index 位置的元素
const arr4 = arr1.delete(1);
// => [1, 3]

// 4. 插入元素到 List
// insert(index: number, value: T)
// 在 index 位置插入 value
const arr5 = arr1.insert(1, 2);
// => [1, 2, 2, 3]

// 5. 清空 List
// clear()
const arr6 = arr1.clear();
// => []
  1. Set:沒有順序且不能重複的列表
const Set= Immutable.Set;

// 1. 建立 Set
const set1 = Set([1, 2, 3]);
// => Set { 1, 2, 3 }

// 2. 新增元素
const set2 = set1.add(1).add(5);
// => Set { 1, 2, 3, 5 } 
// 由于 Set 為不能重複集合,故 1 只能出現(xiàn)一次

// 3. 刪除元素
const set3 = set1.delete(3);
// => Set { 1, 2 }

// 4. 取聯(lián)集
const set4 = Set([2, 3, 4, 5, 6]);
set1.union(set4);
// => Set { 1, 2, 3, 4, 5, 6 }

// 5. 取交集
set1.intersect(set4);
// => Set { 2, 3 }

// 6. 取差集
set1.subtract(set4);
// => Set { 1 }

ImmutableJS 的特性整理

  1. Persistent Data Structure
    ImmutableJS 的世界裡,只要資料一被創(chuàng)建,就不能修改,維持 Immutable。就不會發(fā)生下列的狀況:
var obj = {
 a: 1
};

funcationA(obj);
console.log(obj.a) // 不確定結(jié)果為多少?

使用 ImmutableJS 就沒有這個問題:

// 有些開發(fā)者在使用時會在 ``Immutable` 變數(shù)前加 `$` 以示區(qū)隔。

const $obj = fromJS({
 a: 1
});

funcationA($obj);
console.log($obj.get('a')) // 1
  1. Structural Sharing
    為了維持資料的不可變,又要避免像 deepCopy 一樣複製所有的節(jié)點(diǎn)資料而造成的資源損耗,在 ImmutableJS 使用的是 Structural Sharing 特性,亦即如果物件樹中一個節(jié)點(diǎn)發(fā)生變化的話,只會修改這個節(jié)點(diǎn)和和受它影響的父節(jié)點(diǎn),其他節(jié)點(diǎn)則共享。
const obj = {
  count: 1,
  list: [1, 2, 3, 4, 5]
}
var map1 = Immutable.fromJS(obj);
var map2 = map1.set('count', 4);

console.log(map1.list === map2.list); // true
  1. Support Lazy Operation
Immutable.Range(1, Infinity)
.map(n => -n)
// Error: Cannot perform this action with an infinite size.

Immutable.Range(1, Infinity)
.map(n => -n)
.take(2)
.reduce((r, n) => r + n, 0); 
// -3
  1. 豐富的 API 并提供快速轉(zhuǎn)換原生 JavaScript 的方式
    在 ImmutableJS 中可以使用 fromJS()、toJS() 進(jìn)行 JavaScript 和 ImmutableJS 之間的轉(zhuǎn)換。但由于在轉(zhuǎn)換之間會非常耗費(fèi)資源,所以若是你決定引入 ImmutableJS 的話請盡量維持資料處在 Immutable 的狀態(tài)。

  2. 支持 Functional Programming
    Immutable 本身就是 Functional Programming(函數(shù)式程式設(shè)計(jì))的概念,所以在 ImmutableJS 中可以使用許多 Functional Programming 的方法,例如:map、filter、groupBy、reducefindfindIndex 等。

  3. 容易實(shí)現(xiàn) Redo/Undo 歷史回顧

React 效能優(yōu)化

ImmutableJS 除了可以和 Flux/Redux 整合外,也可以用于基本 react 效能優(yōu)化。以下是一般使用效能優(yōu)化的簡單方式:

傳統(tǒng) JavaScript 比較方式,若資料型態(tài)為 Primitive 就不會有問題:

// 在 shouldComponentUpdate 比較接下來的 props 是否一致,若相同則不重新渲染,提昇效能
shouldComponentUpdate (nextProps) {
    return this.props.value !== nextProps.value;
}

但當(dāng)比較的是物件的話就會出現(xiàn)問題:

// 假設(shè) this.props.value 為 { foo: 'app' }
// 假設(shè) nextProps.value 為 { foo: 'app' },
// 雖然兩者值是一樣,但由于 reference 位置不同,所以視為不同。但由于值一樣應(yīng)該要避免重複渲染
this.props.value !== nextProps.value; // true

使用 ImmutableJS

var SomeRecord = Immutable.Record({ foo: null });
var x = new SomeRecord({ foo: 'app'  });
var y = x.set('foo', 'azz');
x === y; // false

在 ES6 中可以使用官方文件上的 PureRenderMixin 進(jìn)行比較,可以讓程式碼更簡潔:

import PureRenderMixin from 'react-addons-pure-render-mixin';
class FooComponent extends React.Component {
  constructor(props) {
    super(props);
    this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
  }
  render() {
    return <div className={this.props.className}>foo</div>;
  }
}

總結(jié)

雖然 ImmutableJS 的引入可以帶來許多好處和效能的提升但由于引入整體檔案較大且較具侵入性,在引入之前可以自行評估看看是否合適于目前的專案。接下來我們將在后面的章節(jié)講解如何將 ImmutableJSRedux 整合應(yīng)用到實(shí)務(wù)上的范例。

延伸閱讀

  1. 官方網(wǎng)站
  2. Immutable.js初識
  3. Immutable 詳解及 React 中實(shí)踐
  4. 為什么需要Immutable.js
  5. facebook immutable.js 意義何在,使用場景?
  6. React 巢狀 Component 效能優(yōu)化
  7. PureRenderMixin
  8. seamless-immutable
  9. Immutable Data Structures and JavaScript

(image via risingstack

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內(nèi)容