immer.js 一个不可变的、结构共享的数据结构是存储状态的绝佳范例

作者: tww844475003 分类: 前端开发 发布时间: 2022-02-19 21:13

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
    }
  })
前端开发那点事
微信公众号搜索“前端开发那点事”

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注