👨‍💻
Hong's 前端笔记
  • 🌈About
  • JavaScript
    • 作用域
    • 闭包
    • this
      • bind()
    • 原型
      • Object.create()
      • 模拟“类”
      • ES6 Class
      • Function
    • 对象
    • 类型 & 值
      • 原生函数
      • 数组
      • 字符串
    • 异步
    • JavaScript 相关概念
      • Event Loop 事件循环
      • Prototype 原型
      • Context 执行上下文
      • this
      • Promise
    • JavaScript 常见问题
      • 手写实现
      • 看代码输出
  • React
    • 核心概念
    • 深入理解
      • State
      • 如何管理 State
      • React 渲染过程
      • 处理 DOM 事件
      • Pure 组件
      • Context
      • Key 属性
      • React.lazy()
      • Render Props
    • Hooks
      • useState
      • useRef
      • useEffect
        • Some details
      • 相关概念
      • 自定义 Hook
      • useEffect vs useLayoutEffect
    • React 相关问题
      • Class vs Function
  • React Libraries
    • Redux
      • Quick Start
      • 如何持久化 Redux 数据
      • 异步操作
      • 性能优化
      • Dive into immutability
    • Route v6
      • Quick Start
  • Reactivity
    • Immer
  • Vue
    • 深入理解
      • Composition vs Options
      • Reactivity
  • Network
    • Cookies
    • HTTP
    • HTTPS
    • CORS 跨域资源共享
    • 认证手段
    • 安全相关
    • 网络相关知识
  • Browser
    • DOM 操作
    • Events 事件
    • XHR & Fetch
    • 性能优化
    • HTML 相关概念
    • 浏览器相关概念
  • CSS
    • 盒模型
    • Layout 布局
    • Styles 样式
    • CSS 相关概念
    • CSS 相关技巧
      • 移动端适配
      • Flex
      • 动画
  • TypeScript
    • Quick Peek
    • Types in TypeScript
    • Narrowing
    • Functions in TypeScript
  • Workflow
    • Webpack
      • Webpack 概念
      • 资源管理
      • 管理输出
      • 开发环境配置
      • 生产环境配置
      • 优化代码运行性能
  • Others
    • 小程序与原生 Web 的区别
  • SSO
    • About
    • API Doc
    • 接入指南
Powered by GitBook
On this page
  • draft & state
  • produce
  • 柯里化
  • current & original
  • auto freezing
  • 工作原理
Edit on GitHub
  1. Reactivity

Immer

Last updated 2 years ago

draft & state

Immer 在 produce 中会丢给你一份当前 state 的 draft ,你的所有修改都在 draft 上进行。draft 是对原始 state 的一层 proxy。当所有的变更完成后,Immer 根据 draft 上的变更,生成一个新的 nextState

produce

produce(baseState, recipe: (draftState) => void): nextState

柯里化

const foo1 = {
    cnt: 0,
}

const addCnt = produce((draft, n = 1) => {
    draft.cnt += n
})

const foo2 = addCnt(foo1, 2)

// foo1 {cnt: 0}
// foo2 {cnt: 2}

current & original

current 对当前的 draft 创建一份副本,original 对原始 state 创建一份副本

const foo1 = {
    cnt: 0,
}

const foo2 = produce(foo1, draft => {
    draft.cnt++

    const orignalState = origin(draft)
    const snapshot = current(draft)

    console.log(orignalState.cnt)  // 0
    console.log(snapshot.cnt)      // 1
})

current 创建的是一个不含 Proxy 的 Plain object。与直接访问 draft 相比,current 可以安全地泄露到 produce 外部。从另一个角度来说,current 可以当做draft 的某一个阶段的一个 snapshot。

auto freezing

Immer 会自动地 freeze 所有 produce 返回的对象。这样,当你意外地尝试修改一个 state 时,就会抛出异常。从而保证了数据的 immutability。

Immer 采用的是递归 freeze,因此 当数据比较大的时候,可能会造成性能开销。

通过 setAutoFreeze(true / false) 来关闭 Immer 的默认 auto freeze 功能。

工作原理

当 producer 开始执行的时候,只有 draft 的根节点的有一层 proxy 。一旦你往下访问了任意一个非原始数据类型(引用类型)的时候,就会自动为这个结点创建一层 proxy ,最终生成如上图这样的一棵 proxied tree。

当你在对 draft 上的某一个节点尝试修改时,Immer 会对该结点做一个浅拷贝,并且标记为 modified 。之后如果对该结点的任何读写操作,都不会发生在原始 state 上了。

当一个节点被标记为 modified 后,他的所有父节点也会被同样标记为 modified

当整个 producer 完成后,Immer 遍历整颗 proxy tree,如果一个结点被标记为 modified ,那么就复制他的 copy;若没有,则仍然使用原始的结点。

绿圈 - 原始数据;蓝色圆环 - Proxy