第一节:vue3-Reactivity模块-reactive

286 阅读4分钟

readonly代理过的,在被reactive进行代理的时候直接返回readonly的结果

已经被代理过的对象再次代理则返回上次代理的结果

Reactivity模块基本使用

<div id="app"></div>
<script src="./reactivity.global.js"></script>
<script>
    const { reactive, effect, shallowReactive, shallowReadonly, readonly } = VueReactivity;
    // let state = reactive({ name: 'lyp', age: 30 });
    // const state = shallowReactive({ name: 'lyp', age: 30 })
    // const state = readonly({ name: 'lyp', age: 30 })
    const state = reactive({ name: 'lyp', age: 30})
</script>

reactive方法会将对象变成proxy对象, effect中使用reactive对象时会进行依赖收集,稍后属性变化时会重新执行effect函数~。

编写reactive函数

  • 1、将数据变成响应式的, reactive 只能做对象的代理
  • 2、在setget中 用Reflect设置值和取值
  • 3、响应式映射表 防止一个对象多次代理:state1 = reactive(data); state2 = reactive(data)
  • 4、ReactiveFlages 防止 代理对象再次被代理 state1 = reactive(data); state2 = reactive(state1)
  • 5、get的时候 对effect进行依赖收集
  • 6、set的时候 比对新旧值 根据依赖收集更新effect函数
  • 7、get时如果得到的是对象 递归深度代理

reactive接受的参数必须是一个对象类型

import { isObject } from "@vue/shared"
function createReactiveObject(target: object, isReadonly: boolean) {
    if (!isObject(target)) {
        return target
    }
}
// 常用的就是reactive方法
export function reactive(target: object) {
    return createReactiveObject(target, false)
}

已经被代理过对象处理__v_isReactive

const enum ReactiveFlags {
    IS_REACTIVE = '__v_isReactive'
}
const mutableHandlers: ProxyHandler<object> = {
    get(target, key, receiver) {
        // 在get中增加标识,当获取IS_REACTIVE时返回true
        if(key === ReactiveFlags.IS_REACTIVE){ 
            return true;
        }
    }
}
function createReactiveObject(target: object, isReadonly: boolean) {
    // 在创建响应式对象时先进行取值,看是否已经是响应式对象
    if(target[ReactiveFlags.IS_REACTIVE]){ 
        return target
    }
}

要使用Reflect进行操作,保证this指向永远指向代理对象

解决以下问题

let target = {
    name:'zf',
    get alias(){
        return this.name
    }
}
const proxy = new Proxy(target,{
    get(target,key,receiver){
        // 去代理对象上取值 就走get
        // return target[key];
        console.log(key);
        return Reflect.get(target,key,receiver)
    },
    set(target,key,value,receiver){
        // 去代理上设置值 执行set
      
        return Reflect.set(target,key,value,receiver);
    }
});
proxy.alias; // 取alais值时,也取了name,但是没有监控到name

完整版

// __v_isReactive 为代理过对象的标识
export const enum ReactiveFlags {
    IS_REACTIVE = '__v_isReactive' 
}

export const mutableHandlers = {
    get(target, key, receiver) {
        // 第一次普通对象代理,我们通过 new Proxy代理
        // 下一次传递的是proxy 可以看一下有没有代理过 
        // 如果访问这个proxy有get方法的时候 说明代理过了 
        if(key === ReactiveFlags.IS_REACTIVE){
            return true
        }
        // 依赖收集 取值的时候 activeEffect 会和 key关联起来 执行完就没了’
        // 调用的是effect中的方法
        track(target,'get', key)  // 调用的是effect中的方法
        
        // 去代理对象上取值
        // 可以监控到取值操作
        let res=Reflect.get(target, key, receiver)
        // 递归深度代理
        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){  // 值变化了
            // 要更新有依赖的 effect
            // 调用的是effect中的方法
            trigger(target,'get', key, value, oldValue)
        }
        // 可以监控到设置值操作
        return result
    }
}

// 响应式映射表 防止一个对象多次代理:state1 = reactive(data) state2 = reactive(data) 
// key只能是对象  
const reactiveMap = new WeakMap() 

export function reactive(target) {
    //只对对象进行代理(除了null)
    if (!isObject(target)) {
        return
    }
    // 如果目标是一个代理过的对象 会触发 proxy中的get  会返回true,
    //没代理过的就不会走到proxy中的get
    if(target[ReactiveFlags.IS_REACTIVE]){ 
        return target
    }
    // 并没有重新定义属性 只是代理 在取值的时候会调用get
    let exisitingProxy = reactiveMap.get(target)
    if(exisitingProxy) return exisitingProxy;

    const proxy = new Proxy(target, mutableHandlers)
    reactiveMap.set(target,proxy)
    return proxy;

}

toRaw将响应式对象转成非响应式的

let obj={name:'lyp'}
let proxy = reactive(obj) // target ->  proxy
console.log(obj === toRaw(proxy)) // proxy -> target

markRaw 标记对象不可被代理

let obj={name:'lyp'}
let proxy = reactive(markRaw(obj)) // 标记对象不可被代理
console.log(proxy) //__v_skip:true 表示不能被代理 使用Object.defineProperty实现

在代理数组的时候

如果是数组 对 includes indexOf lastIndexOf pop shift push splice unshift 方法进行重写

const data = {name: 'lyp'}
const arr = reactive([data]) // 对象中嵌套对象 在取值的时候会进行递归处理
console.log(data === arr[0]) // 这里访问arr[0]的时候获取的是代理对象

// 这里调用includes vue内部会将代理对象转换成原始对象,在处理arr.includes时 原始的不行再去调用代理过的
console.log(arr.includes(data)) // 在正常情况下不应该为 true,所以要重写includes方法

在访问symbol属性和__proto__的时候 不做依赖收集处理直接返回属性值

//  下边的访问不会做依赖收集
console.log(state[Symbol.iterator])
console.log(state[Symbol.__proto__)