文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

immer.js原理是什么

2023-07-05 05:16

关注

这篇文章主要介绍了immer.js原理是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇immer.js原理是什么文章都会有所收获,下面我们一起来看看吧。

但如果拷贝的是一个树形结构,层次比较深,看是一个对象,但实际上里面有上百个对象,比如:

// 某某公司组织架构const org = {    name: "某某公司",    children: [        { name: "研发部", children: [{ name: "张三" }, { name: "李四" }] },        { name: "产品部", children: [{ name: "王五" }] },        // 省略 10 个部门,每个部门 10 个人    ]}

这个 org 数据中的 children 是 Array 类型的对象,children 里面的部门一个是一个基本对象,然后再往下又是 Array 对象 ...... ,上面结构看起来还很简单,但实际上写出来的都有了 9 个对象,如果这个组织有一百个人,至少 100 多个对象,如果为了保持数据不可变,每次修改对象,都要对整个 org 进行拷贝的话,那么操作个几十次上百次,很容易造成性能问题,要是原始的数据意外没有销毁的话,还容易造成内存泄露(这是我曾经刚出来工作一两年干过的事情,操作一个增删改查的列表页,没操作几次,浏览器就变卡了,到后面必须得重新刷新页面????️)。

因此,当数据规模大、数据拷贝行为频繁时,拷贝将会给我们的应用性能带来巨大的挑战。

于是社区出现了很多来让可变数据不可变的方案,核心目的都是为了 从最小单元去进行拷贝,没改变的对象数据则进行复用,而其中最具有代表性和影响力的就是 immutable.jsimmer.js

immutable.js 底层是持久化数据结构,内部实现比较复杂,相比而言,immer.js 的底层是 Proxy 代理模式,这种方式的实现过程比 immutable.js 会简单不少。

了解 immer.js

immer.js 最重要最核心的就是 produce 函数,也是默认导出函数,其他的导出其实都算是一些辅助性工具函数。

下面我们来看一下 produce 的使用示例,验证它是不是实现了 从最小单元去进行拷贝,没改变的对象数据则进行复用 这个目的。

import produce from "immer"const state = [    { label: "HTML", info: { desc: "超文本标记语言" } },    { label: "CSS", info: { desc: "层叠样式表" } }];const state1 = produce(state, draft => {     // 新增了一个对象    draft.push({ label: "ES5", info: { desc: "基于原型和头等函数的多范式高级解释型编程语言" } });    // 修改了了一个对象    draft[1].label = "CSS3";})console.log(state === state1) // falseconsole.log(state.length === state1.length) // falseconsole.log(state[0] === state1[0]) // trueconsole.log(state[1] === state1[1]) // falseconsole.log(state[1].info === state1[1].info) // true

可以看出来,每个最小单元的对象,如果进行了修改,则会拷贝对象,如果没有进行修改的对象,则会进行复用。

我们把它画成一个图:

immer.js原理是什么

因此,只要对子节点的任何操作,实际上都会拷贝当前对象,当前对象被拷贝,就会影响上一层的对象也会被拷贝,层层递进,最后拷贝到了根结点,但是都是浅拷贝,因此子节点没有变的对象都可以复用。

比如我再修改一下:

const state2 = produce(state1, draft => {    draft[2].label = "ES";})

这时候的情况就是这样:

immer.js原理是什么

immer.js 原理

immer.js 是基于 Proxy 来监听对象的 get 和 set 操作,然后对数据进行处理和判断是否返回新的对象。

我们来使用 Proxy 来进行模拟 produce 函数。

function produce<D extends object>(base: D, recipe: (draft: D) => void) {  // 用于存储改变后的新数据  let newData: any;  // 给 base 对象添加代理  const proxy = new Proxy(base, {    set(obj, key: string, value: any) {      // 检查 newData 是否存在,如果不存在,创建 newData      if (!newData) {        // 浅拷贝对象        newData = { ...obj }      }      // 修改 newData,而不是 base,永远不要修改 base      newData[key] = value      return true    }})  // 将 对象的代理 作为入参传入 recipe,让外界修改的是代理,而不是原本的对象数据  recipe(proxy)  // 为了避免意外的修改发生,返回一个被“冻结”的对象,保证数据的纯度  // 如果 newData 不存在,表示没有执行写操作,返回 base 即可  return Object.freeze(newData as D || base)}

然后我们来测试一下:

const state = { label: "HTML", info: { desc: "超文本标记语言" } };const state1 = produce(state, (draft) => {    draft.label = "H5";})console.log(state === state1) // falseconsole.log(state.info === state1.info) // true

可以看出实现的这个极简版的 produce 已经可以实现 从最小单元去进行拷贝,没改变的对象数据则进行复用,但仅限于修改对象的第一层结构,如果直接修改 draft.info.desc 会发现 state 和 state1 都会被改变。

Proxy 只会对当前传入进去的一个对象单元进行代理,如果有子对象,并不会进行代理,因此,深层次对象还需要再加处理,就像深拷贝一样,需要进行递归处理。

immer.js 源码的代码并不少,主要是为了兼容性、处理各种数据类型、以及扩展API,因此做了很多处理,这个后续会单独出一篇分析它内部源码的实现,这里先说一下其内部主要方案:

关于“immer.js原理是什么”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“immer.js原理是什么”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注编程网行业资讯频道。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯