immer.js 一个不可变的、结构共享的数据结构是存储状态的绝佳范例
Immer 是 mobx 的作者写的一个 immutable 库,核心实现是利用 ES6 的 proxy,几乎以最小的成本实现了 js 的不可变数据结构,简单易用、体量小巧、设计巧妙,满足了我们对JS不可变数据结构的需求。
在日常开发中,数据的值引用,浅拷贝会修改原始数据困扰了众多开发者。一不留心就产生了BUG,性能问题。
immutable.js 很全面,但是有一点很不好,门槛高,不易懂。immer.js刚好解决了这个问题。
不可变的、结构共享的数据结构是存储状态的绝佳范例。尤其是与事件溯源架构结合使用时。但是,要付出一定的代价。在像JavaScript这样的语言中,不可变性没有内置在语言中,从前一个状态生成新状态是一项无聊的、样板式的任务。为了证明这一点:仅Redux-ecosystem-links页面就列出了 67(!) 个包来帮助您处理 Redux 中的不可变数据结构。
他们中的大多数都没有解决根本问题:缺乏语言支持。例如,在像 ClojureScript 这样的语言中,update-in 是一个优雅的概念,任何 JavaScript 对应物基本上都将依赖于丑陋的字符串路径。这很容易出错,难以进行类型检查,并且需要认真学习另一组 API 函数才能提高效率。
可变数据结构
const variableData = {
name: 'variable',
num: 1,
p: {
x: 1,
},
};
const v1 = variableData;
v1.name = 'v1';
v1.num = 2;
v1.desc = 'description';
const v2 = {
attrs: 'attrs',
...variableData,
};
v2.name = 'v2';
v2.num = 3;
v2.desc = 'new description';
v2.p.x = 3;
console.log('v1', v1);
console.log('v2', v2);
console.log('variable', variableData);
Immer 不可变数据结构
import produce from "immer"
const nextState = produce(currentState, draft => {
// empty function
})
console.log(nextState === currentState) // true
const variableData = {
name: 'variable',
num: 1,
p: {
x: [1],
},
};
const v1 = produce(variableData, (draft) => {
draft.p.x = 2; // eslint-disable-line
});
const v2 = produce(variableData, (draft) => {
draft.p.x.push(2);
draft.p.x.push(3);
});
console.log('v1', v1);
console.log('v2', v2);
console.log('variableData', variableData);
Redux reducer 中使用
const byId = (state, action) =>
produce(state, draft => {
switch (action.type) {
case RECEIVE_PRODUCTS:
action.products.forEach(product => {
draft[product.id] = product
})
break
}
})