reactive的实现
标签:set,const,target,reactive,源码,key,vue3,return From: https://www.cnblogs.com/dgqp/p/17364484.html
使用:
使用
reactive()
函数创建一个响应式对象。import { reactive } from 'vue' export default { // `setup` 是一个专门用于组合式 API 的特殊钩子函数 setup() { const state = reactive({ count: 0 }) // 暴露 state 到模板 return { state } } }
<div>{{ state.count }}</div>
手写源码
这里有四个方法需要实现包括
reactive, shallowReactive, readonly, shallowReadonly
这个四个方法的实现:
export function reactive(target) { return createReactiveObject(target, false, mutabelHandlers); } export function shallowReactive(target) { return createReactiveObject(target, false, shallowReactiveHandlers); } export function readonly(target) { return createReactiveObject(target, true, readonlyHandlers); } export function shallowReadonly(target) { return createReactiveObject(target, true, shallowReadonlyHandlers); }
这四个方法同一调用一个创建函数,不同的是根据功能给出相应的参数。
其中
target
是劫持的对象,第二个参数是是否是仅读的,第三个是处理的handler
四个
handler
实现:import { extend, hasChanged, hasOwn, isAarray, isIntegerKey, isObject, } from "@vue/shared"; import { reactive, readonly } from "./reactive"; import { track, trigger } from "./effect"; // 实现new Proxy(target,handler) // 是不是仅读的,仅读属性set时会报异常 // 是不是深度的 function createGetters(isReadonly = false, shallow = false) { return function get(target, key, receiver) { // proxy + reflect // 后续object方法会被迁移到Reflect Reflect.getPropertyeof() // 以前target[key] = value 方法设置值可能会失败,并不会报异常,也没有返回值标识 // Reflect方法具备返回值 // reflect使用可以不使用 proxy es6 const res = Reflect.get(target, key, receiver); if (!isReadonly) { // 收集依赖 track(target, TrackOpTypes.GET, key); } if (shallow) { return res; } if (isObject(res)) { // vue2是一上来就递归,vue3是当取值的时候会进行代理,vue3的代理模式是懒代理 return isReadonly ? readonly(res) : reactive(res); } return res; }; } // 拦截获取功能 function createSetter(shallow = false) { return function set(target, key, value, receiver) { const oldValue = target[key]; let hadKey = isAarray(target) && isIntegerKey(key) ? Number(key) < target.length : hasOwn(target, key); const res = Reflect.set(target, key, value, receiver); // s数据更新时视图更新, 通知对应属性的effect重新执行 if (!hadKey) { // 新增 trigger(target, TriggerOrTypes.ADD, key, value); } else if (hasChanged(oldValue, value)) { // 修改 trigger(target, TriggerOrTypes.SET, key, value, oldValue); } return res; }; } // 拦截设置功能 const get = createGetters(); const shallowGet = createGetters(false, true); const readonlyGet = createGetters(true); const shallowReadonlyGet = createGetters(true, true); const set = createSetter(); const shallowSet = createSetter(true); // reactive的 export const mutabelHandlers = { get, // get只是获取,无特殊参数 set: set, // set需要,一样的 }; export const shallowReactiveHandlers = { get: shallowGet, // 浅的的需要给浅的参数 set: shallowSet, // 需要给一个浅的的参数 }; let readonlyObj = { // 只读如果取值,就报错 set: (target, key) => { console.warn(`set on key ${key} failed`); }, }; export const readonlyHandlers = extend( // 只读get访问只读的,set报错 { get: readonlyGet, }, readonlyObj ); export const shallowReadonlyHandlers = extend( //同上 { get: shallowReadonlyGet, }, readonlyGet );
目的是重写相应的
get
和set
方法。其实使用的都是工厂函数。然后就要创建响应式数据,就要根据不同的参数调用
createReactiveObject
方法const reactiveMap = new WeakMap(); // 会自动垃圾回收,存储的key只能是对象 const readonlyMap = new WeakMap(); // export function createReactiveObject(target, isReadonly, baseHandlers) { // 如果目标不是对象,没法拦截了,reactive这个api只能拦截对象 if (!isObject(target)) { return target; } // 如果某个对象被代理过了,就不要再次代理 const proxyMap = isReadonly ? readonlyMap : reactiveMap; const existProxy = proxyMap.get(target); if (existProxy) { return existProxy; } const proxy = new Proxy(target, baseHandlers); proxyMap.set(target, proxy); return proxy; }
使用的也是工厂函数实现代理的,数据代理的。
因此这里的数据响应式处理使用的是
Proxy
,其中这里还对对象是否代理进行了处理,使用的是WeakMap
。这样就可以做到了响应式原理。