JS-Vue响应式原理

111 阅读2分钟

Vue2响应式原理

  • 通过Object.defineProperty的方式实现对象属性的监听

    // 保存当前需要收集的响应式函数
    let activeReactiveFn = null;
    
    // 封装依赖收集类
    class Depend {
        constructor() {
            // 使用Set防止函数重复添加
            this.reactiveFns = new Set();
        }
        depend() {
            if (activeReactiveFn) {
                this.reactiveFns.add(activeReactiveFn);
            }
        }
        notify() {
            this.reactiveFns.forEach((fn) => fn());
        }
    }
    
    // 封装响应式函数
    function watchFn(fn) {
        activeReactiveFn = fn;
        fn();
        activeReactiveFn = null;
    }
    
    // 封装获取depend的函数
    const targetMap = new WeakMap();
    function getDepend(target, key) {
        // 根据target对象获取map
        let map = targetMap.get(target);
        if (!map) {
            map = new Map();
            targetMap.set(target, map);
        }
    
        // 根据key获取depend对象
        let depend = map.get(key);
        if (!depend) {
            depend = new Depend();
            map.set(key, depend);
        }
    
        return depend;
    }
    
    // 监听对象的属性变量:proxy(vue3)/Object.defineProperty(vue2)
    function reactive(obj) {
        Object.keys(obj).forEach(key => {
            let value = obj[key]
            Object.defineProperty(obj, key, {
                get() {
                    const depend = getDepend(obj, key)
                    depend.depend()
                    return value
                },
                set(newValue) {
                    value = newValue
                    const depend = getDepend(obj, key)
                    depend.notify()
                }
            })
        })
        return obj
    }
    
    // 将对象变为响应式,一个属性对应一个depend对象
    const objProxy = reactive({
        name: "zzy", //
        age: 22,
    });
    const infoProxy = reactive({
        address: "广州市",
    });
    
    watchFn(() => {
        console.log(objProxy.name);
    });
    watchFn(() => {
        console.log(infoProxy.address);
    });
    
    objProxy.name = 'lll'
    infoProxy.address = "北京市";
    
    

Vue3响应式原理

  • 通过Proxy的方式来监听数据的变化以及收集相关依赖

    // 保存当前需要收集的响应式函数
    let activeReactiveFn = null;
    
    // 封装依赖收集类
    class Depend {
        constructor() {
            // 使用Set防止函数重复添加
            this.reactiveFns = new Set();
        }
        depend() {
            if (activeReactiveFn) {
                this.reactiveFns.add(activeReactiveFn);
            }
        }
        notify() {
            this.reactiveFns.forEach((fn) => fn());
        }
    }
    
    // 封装响应式函数
    function watchFn(fn) {
        activeReactiveFn = fn;
        fn();
        activeReactiveFn = null;
    }
    
    // 封装获取depend的函数
    const targetMap = new WeakMap();
    function getDepend(target, key) {
        // 根据target对象获取map
        let map = targetMap.get(target);
        if (!map) {
            map = new Map();
            targetMap.set(target, map);
        }
    
        // 根据key获取depend对象
        let depend = map.get(key);
        if (!depend) {
            depend = new Depend();
            map.set(key, depend);
        }
    
        return depend;
    }
    
    // 监听对象的属性变量:proxy(vue3)/Object.defineProperty(vue2)
    function reactive(obj) {
        return new Proxy(obj, {
            get(target, key, receiver) {
                console.log("get被访问", key, target);
                // 根据target和depend获取对应的depend
                const depend = getDepend(target, key);
                // 给depend对象中添加响应函数
                depend.depend();
    
                return Reflect.get(target, key, receiver);
            },
            set(target, key, newValue, receiver) {
                console.log("set被访问", key, target, newValue);
                Reflect.set(target, key, newValue, receiver);
                const depend = getDepend(target, key);
                depend.notify();
            },
        });
    }
    
    // 将对象变为响应式,一个属性对应一个depend对象
    const objProxy = reactive({
        name: "zzy", //
        age: 22,
    });
    const infoProxy = reactive({
        address: "广州市",
    });
    
    watchFn(() => {
        console.log(objProxy.name);
    });
    watchFn(() => {
        console.log(infoProxy.address);
    });
    
    objProxy.name = 'lll'
    infoProxy.address = "北京市";