跳到主要内容

认识

2024年03月11日
柏拉文
越努力,越幸运

一、认识


Immer 实现的原理是: 基于 Proxy 来代理对象的各种操作,然后在数据进行操作时,Proxy 会去拷贝被修改对象,然后再进行数据操作,返回拷贝后并被修改的数据。 当数据被修改时,会返回一个对象,但是新的对象会尽可能的利用之前的数据结构而不会对内存造成浪费, 也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变,同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗,Immer 使用了(结构共享)。如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享。

二、工作


  1. 使用 Proxy 来实现对对象属性的 增删改查 的监听

  2. 使用 handler.get 来拦截对象的读取属性操作,查到的属性值如果是一个对象,那么需要对该象进行代理,也就是对子对象也要使用 Proxy 来管理,因此需要记录每一个被 Proxy 的对象,这里将会新增 proxies 内部状态来存储原对象和代理对象的关系。

  3. 使用 handler.set 来监听 ,因为被代理的对象每一次修改对象的属性值,都会被 proxy 监听到,监听到修改后需要对对该对象进行拷贝 ,然后对拷贝的对象进行修改,而不直接修改原对象,永远不要直接修改原对象,因此需要记录每一个被拷贝的对象,这里将会新增 copies 内部状态来存储原对象和拷贝对象的关系。

  4. 使用 handler.has 来监听 in 操作符的查找方式。

  5. 使用 handler.ownKeys 来拦截 Object.getOwnPropertyNames()Object.getOwnPropertySymbols() 属性获取,也可以拦截 Object.keys()Reflect.ownKeys()

  6. 使用 handler.deleteProperty 来拦截删除操作,有拷贝的删除拷贝的属性,不能删除原对象。

2.2 handler get

使用 handler.get 来拦截对象的读取属性操作,查到的属性值如果是一个对象,那么需要对该象进行代理,也就是对子对象也要使用 Proxy 来管理,因此需要记录每一个被 Proxy 的对象,这里将会新增 proxies 内部状态来存储原对象和代理对象的关系。需要注意的是: 同一个属性可能被多次获取,并且被修改过,因此要优先判断是否被拷贝过,有拷贝直接从拷贝对象中去拿取属性,然后对拷贝的对象的属性进行设置代理(对象才会被代理,其他基本数据类型会直接返回)

2.3 handler set

使用 handler.set 来监听 ,因为被代理的对象每一次修改对象的属性值,都会被 proxy 监听到,监听到修改后需要对对该对象进行拷贝 ,然后对拷贝的对象进行修改,而不直接修改原对象,永远不要直接修改原对象,因此需要记录每一个被拷贝的对象,这里将会新增 copies 内部状态来存储原对象和拷贝对象的关系。需要注意如果值没有变化,则不需要进行拷贝。

2.4 handler has

使用 handler.has 来监听 in 操作符的查找方式。

2.5 handler delete

使用 handler.deleteProperty 来拦截删除操作,有拷贝的删除拷贝的属性,不能删除原对象。

2.6 handler ownKeys

使用 handler.ownKeys 来拦截 Object.getOwnPropertyNames()Object.getOwnPropertySymbols() 属性获取,也可以拦截 Object.keys()Reflect.ownKeys()

2.7 finalize

finalize 处理数据, 返回修改后的数据。基本思路: 如果数据没有改变, 直接原始数据。通过获取原始数据副本, 递归处理数据。

2.8 hasChanges

hasChanges 判断数据是否被改变。基本思路: 检测传入对象是否被代理, 如果没有被代理, 一定没有修改过(被代理的对象不一定被修改,被修改的一定被代理和拷贝)。随后检测传入对象是否被拷贝, 如果拷贝了, 那一定被改变了(被拷贝的对象,一定被修改,但子对象不一定被修改,因此后面进行了递归检查)。最后, 递归检查子数据。

2.9 createProxy

createProxy 用于创建代理, 被代理过的对象避免重复被代理,只有普通对象和数组才需要被代理。同一个属性可能被多次获取,并且被修改过,因此要优先判断是否被拷贝过,有拷贝直接从拷贝对象中去拿取属性,然后对拷贝的对象的属性进行设置代理(对象才会被代理,其他基本数据类型会直接返回)

2.10 getCurrentSource

被代理的对象不一定被修改,被修改的一定被代理和拷贝。数据如果被修改了很多次,那么我们获取正确数据的方法就是先查找数据是否被拷贝,被拷贝说明一定被修改了,没有被拷贝,说明没有被修改,因此实现一个 getCurrentSource 方法来查找对象,需要获取最新对象直接使用这个方法来查找

2.11 getOrCreateCopy

getOrCreateCopy 获取 copy 元对象,没有则进行创建。