天天看點

Immutable vs. seamless-immutable?可變性方案之争!

作者:前端進階

大家好,很高興又見面了,我是"前端‬進階‬",由我帶着大家一起關注前端前沿、深入前端底層技術,大家一起進步,也歡迎大家關注、點贊、收藏、轉發!

Immutable vs. seamless-immutable?可變性方案之争!

前端‬進階‬

今天給大家帶來的是Immutable vs. seamless-immutable的可變性方案之争,話不多少,直接開始。

1.什麼是不變性?

Immutable vs. seamless-immutable?可變性方案之争!

圖檔來自Priya Pedamkar的“Mutable vs Immutable Java”

不變性是 Clojure 或 Lisp 等函數式語言中最著名的概念,本質是無論如何都不能改變資料。 但這是否意味着一旦建立了資料就無法更改它? 不,其實不然!隻是意味着,每次想要更改資料中的任何内容時,都必須建立完整的副本,然後将更改應用到副本中再傳回,即将所有資料結構視為以隻讀模式工作,而不能直接在上面修改,進而避免一些可能的問題。 比如下面的例子:

const arr = [1, 2, 3];
//想将數組中的第二個元素更改為 4,
//但也希望資料不可變,該怎麼辦?
const changedArr = [...arr.slice(0, 1), 4, ...arr.slice(2)];
// 下面是對象
const obj = {
  foo: 'bar',
};
const changedObj = {
  ...foo,
  // 建立副本
  bar: 'baz',
};           

2.為什麼不可變性如此重要?

将代碼中的每個資料結構都設定為不可變,可以讓代碼完全按照開發者想要的方式運作。 在 JavaScript 世界中,主要使用對值的引用,而不是值本身。 這意味着,每個有權通路引用的代碼都可以更改它引用的值:

const foo = {
  bar: 'baz',
  // bar屬性為baz字元串
};           
someMysteriousFunction(foo); 
// 真的相信這個函數,它不會改變foo對象嗎?
console.log(foo.bar); 
// 能确定它會列印“baz”嗎?不能!           

在上面的示例中, someMysteriousFunction 可能會改變 foo 屬性。 這意味着開發者不能相信這個函數,不能相信任何可以通路該對象引用的代碼, 除非它是不可變的。

Immutable vs. seamless-immutable?可變性方案之争!

不變性在 Redux 的上下文中尤為重要

不變性在 Redux 的上下文中尤為重要,因為它使 Redux 能夠正确且快速地工作。 Redux(和 react-redux)使用淺層相等比較來決定狀态群組件的更新,是以Redux 要求 reducer 是沒有副作用的純函數。

這意味着你必須從 reducer 傳回一個新狀态,而不是改變目前狀态。 這個約定使 Redux 變得快速、可靠,它甚至可以啟用時間旅行調試等進階功能。

3.Immutable.js現有問題?

上文已經知道不可變性是一個有價值的概念,而 Immutable.js通過引入自己的List, Stack, Map, OrderedMap, Set, OrderedSet 和Record等自定義對象,同時内置了對象的方法,也确實做到了這一點。

const { Map } = require('immutable');
// Map
const map1 = Map({ a: 1, b: 2, c: 3 });
const map2 = map1.set('b', 50);
map1.get('b') + ' vs. ' + map2.get('b'); 
// 2 vs. 50           

然而,Immutable.js因為本身的設計問題,依然有一些和開發者無法調和的沖突。

  • 大量樣闆檔案:在處理對象、數組時不能使用标準文法,而是必須使用 API
  • 大多數庫與它不相容:必須廣泛使用 fromJS 和 toJS 工具方法
  • 修改成本高:因為它的文法存在于整個代碼庫中
  • 性能提升不明顯:在絕大多數使用它的應用程式中很少真正有預期的性能提升
  • 明顯拖慢開發速度
Immutable vs. seamless-immutable?可變性方案之争!

圖檔來自Aleksandar Ginovski的《4 Reasons To Love ImmutableJS》

4.Immutable.js的可行替代方案

既然Immutable.js有這麼多問題,那麼開發者應該如何解決呢?即,如何在項目中能有效的使用不可變的特性。簡單場場景可以使用Rest替代,在複雜場景強烈建議使用seamless-immutable。

4.1 對象Rest運算符

Rest符的工作方式類似于 Object.assign ,但更加簡潔。 它在所有現代 JavaScript 運作時中都是開箱即用的,也可以使用 Babel 等工具将其轉換為舊的 ES5 代碼。

const reducer = (state, action) => ({
  ...state,
  myChangingProp: action.payload,
});           

它是一種非常簡單和優雅的文法,而且不需要使用任何import,開箱即用!當有更複雜的對象樹并且想要更改嵌套很深的屬性時,它的問題就出現了:

const reducer = (state, action) => ({
  ...state,
  nestedObj: {
    // 嵌套複制的麻煩
    ...state.nestedObj,
    anotherNestedObj: {
      ...state.nestedObj.anotherNestedObj,
      finallyAPropToChange: action.payload,
    },
  },
});           

4.2 seamless-immutable

seamless-immutable是向後相容普通數組和對象的不可變 JS 資料結構。 可以在 for 循環中使用它們,或将它們傳遞給需要普通 JavaScript 資料結構的函數等。

var array = Immutable(["totally", "immutable", {hammer: "Can’t Touch This"}]);
array[1] = "I'm going to mutate you!"
array[1] // "immutable"
array[2].hammer = "hm, surely I can mutate this nested object..."
array[2].hammer // "Can’t Touch This"
for (var index in array) { console.log(array[index]); }
// "totally"
// "immutable"
// { hammer: 'Can’t Touch This' }
JSON.stringify(array) 
//'["totally","immutable",{"hammer":"Can’t Touch This"}]'           

注意:seamless-immutable的向後相容性要求 Object.defineProperty 和 Object.freeze 等 ECMAScript 5 功能存在并正常工作。看一個使用seamless-immutable操作資料的示例:

import Immutable from 'seamless-immutable';
const initialState = Immutable({});
const reducer = (state, action) =>
  state.setIn(['nestedObj', 'anotherNestedObj', 'finallyAPropToChange'], action.payload);           

在seamless-immutable中,狀态state不再是普通的 JavaSript 對象,而是通過Immutable包裝後的對象,同時還必須使用 API 來進行更改操作。

雖然必須使用 API 來“改變”資料,但可以使用标準的點或括号表示法來通路它,也是因為seamless-immutable向後相容普通 JS 對象和數組。

5.Immutable.js vs. seamless-immutable

包體積大小&加載速度

從包體積來看,immutable壓縮後體積達到了17.7k,而seamless-immutable隻有2.7k,前者是後者的6.5倍。同時,在包的下載下傳速度上,3G網絡下immutable需要353ms,4G下也需要20ms,遠大于seamless-immutable在3G下的54ms和4G網絡下的3ms。

Immutable vs. seamless-immutable?可變性方案之争!

Github/NPM的資料

從Github的資料來看,immutable建立于10年前,seamless-immutable建立于8年前,前者的star數達到了32,454,而後者隻有5,368。是以,從整體看來,seamless-immutable相對比較年輕,在知名度上也會稍微差一點。

Immutable vs. seamless-immutable?可變性方案之争!

同時,放開來看過去5年的資料,immutable處于急劇的爆發期,而seamless-immutable則相對平緩很多。

Immutable vs. seamless-immutable?可變性方案之争!

從周下載下傳量來看immutable也遠勝于seamless-immutable。

Immutable vs. seamless-immutable?可變性方案之争!

seamless-immutable vs. immutable周下載下傳量

綜合來看,如果對于項目有嚴格的性能要求,非常建議大家使用seamless-immutable而不是immutable。雖然seamless-immutable知名度稍低于immutable,但是不失為一個優秀的可變性解決方案。

6.本文總結

本文主要和大家介紹Immutable.js的可行替代方案seamless-immutable,文章從Github資料、NPM周下載下傳量、使用趨勢、性能等諸多元度進行了對比,相信大家對兩者已經有一個比較深入的了解。文末的參考資料提供了大量優秀文檔以供學習,如果有興趣可以自行閱讀。如果大家有什麼疑問歡迎在評論區留言。

參考資料

https://leocode.com/development/you-may-not-need-immutable-js/

https://www.educba.com/mutable-vs-immutable-java/

https://www.npmjs.com/package/seamless-immutable

https://www.npmjs.com/package/immutable