- A+
专栏分享:vue2源码专栏,vue3源码专栏,vue router源码专栏,玩具项目专栏,硬核?推荐?
欢迎各位ITer关注点赞收藏???
在学习 Vue3 是如何进行对象的响应式代理之前,我想我们应该先去了解下 ES6 新增的API Proxy
与 Reflect
,可参考【Vue3响应式入门#02】Proxy and Reflect 。之后我们再手写下 reactive 和 effect 的源码
Reactive
定义: 接收一个普通对象然后返回该普通对象的响应式代理。等同于 2.x 的 Vue.observable()
const obj = reactive({ count: 0 })
响应式转换是“深层的”:会影响对象内部所有嵌套的属性。基于 ES6 的 Proxy 实现,返回的代理对象不等于原始对象。建议仅使用代理对象而避免依赖原始对象。
reactive.ts
Vue3中响应数据核心是 reactive , reactive 中的实现是由 proxy 加 effect 组合,先来看一下 reactive 方法的定义
import { isObject } from '@vue/shared' import { mutableHandlers, ReactiveFlags } from './baseHandler' // key只能是对象;弱引用,更有效的垃圾回收、释放内存 - https://www.zhangxinxu.com/wordpress/2021/08/js-weakmap-es6/ const reactiveMap = new WeakMap() /** * @desc 将数据转化成响应式的数据 */ export function reactive(target) { // issue1 if (!isObject(target)) { return } // issue2 if (target[ReactiveFlags.IS_REACTIVE]) { return target } // issue3 let existingProxy = reactiveMap.get(target) if (existingProxy) { return existingProxy } const proxy = new Proxy(target, mutableHandlers) reactiveMap.set(target, proxy) return proxy }
-
@issue1 只能做对象的代理,不是对象,return
-
@issue2 代理对象被再次代理 可以直接返回代理对象
我们可以利用 Proxy 的 get方法,来判断他有没有代理过
如果访问这个对象的
__v_isReactive
属性,有值就说明代理过了,当然,我们可以约定__v_isReactive
为任何字段 -
@issue3 同一个对象代理多次,返回同一个代理
用 WeakMap去缓存对象和代理对象的映射关系
代理完成时,将此对象和代理对象添加到 WeakMap缓存中;在代理之前,去 WeakMap中读取此对象是否有代理对象的映射,若存在,则返回缓存中的代理对象
WeakMap:key只能是对象;弱引用,更有效的垃圾回收、释放内存,详情请参考JS WeakMap应该什么时候使用 « 张鑫旭-鑫空间-鑫生活
baseHandler.ts
mutableHandlers 是 Proxy 的第二个参数 handler对象提取封装而来
- track () 依赖收集
- trigger () 触发依赖
这两个函数为 effect 里的方法,effect 为 reactive 的核心,后面我们会详细介绍。先来看一下mutableHandlers 对象的定义
import { track, trigger } from './effect' export const enum ReactiveFlags { IS_REACTIVE = '__v_isReactive', } export const mutableHandlers = { // 这里可以监控到用户取值了 get(target, key, receiver) { if (key === ReactiveFlags.IS_REACTIVE) { return true } track(target, 'get', key) let res = Reflect.get(target, key, receiver) // @issue1 // 深度代理实现, 性能好 取值就可以进行代理 if (isObject(res)) { return reactive(res) } return res }, // 这里可以监控到用户设置值了 set(target, key, value, receiver) { let oldValue = target[key] // 缓存老值 let result = Reflect.set(target, key, value, receiver) if (oldValue !== value) { // 值变化了,触发依赖 trigger(target, 'set', key) } return result }, }
-
@issue1 嵌套对象深度代理。只有在取值时,才会进行深度代理,性能好
举个例子,看如下代码。当我们对嵌套对象
product.rate
进行取值时,就会触发 get劫持,然后深度代理嵌套对象product.rate
const product = reactive({ price: 5, quantity: 2, rate: { value: 0.9 } })
shared.ts
共享模块
// 判断是否是JS对象 export const isObject = function(value){ return typeof value === 'object' && value !== null }