大家好,很高興又見面了,我是"前端進階",由我帶着大家一起關注前端前沿、深入前端底層技術,大家一起進步,也歡迎大家關注、點贊、收藏、轉發!
前端進階
今天給大家帶來的是Immutable vs. seamless-immutable的可變性方案之争,話不多少,直接開始。
1.什麼是不變性?
圖檔來自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 屬性。 這意味着開發者不能相信這個函數,不能相信任何可以通路該對象引用的代碼, 除非它是不可變的。
不變性在 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 工具方法
- 修改成本高:因為它的文法存在于整個代碼庫中
- 性能提升不明顯:在絕大多數使用它的應用程式中很少真正有預期的性能提升
- 明顯拖慢開發速度
圖檔來自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。
Github/NPM的資料
從Github的資料來看,immutable建立于10年前,seamless-immutable建立于8年前,前者的star數達到了32,454,而後者隻有5,368。是以,從整體看來,seamless-immutable相對比較年輕,在知名度上也會稍微差一點。
同時,放開來看過去5年的資料,immutable處于急劇的爆發期,而seamless-immutable則相對平緩很多。
從周下載下傳量來看immutable也遠勝于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