响应式原理摘抄笔记

117 阅读1分钟

vue2之前使用Object.defineProperty,定义属性描述符,对属性的get和set进行劫持,实现数据响应式,数组类型数据通过覆盖原型方法重写,劫持变异方法实现响应式

let fns = []
let olaArrayPrototype = Array.prototype;
const proto = Object.create(olaArrayPrototype);
['unshift', 'shift', 'push', 'pop', 'splice', 'sort', 'reverse'].forEach(method => {
    proto[method] = () => {
        fns.forEach(item => item())
        olaArrayPrototype[method].call(this, ...arguments)
    }
})
function effect(fn) {
    fns.push(fn)
    return () => {
        fns = fns.filter(item !== fn)
    }
}

function reactive(data) {
    if (Array.isArray(data)) {
        data.__proto__ = proto
    }
    if (typeof data !== 'object' || data == null) {
        return data;
    }
    Object.keys(data).forEach(key => {
        let value = data[key]
        Object.defineProperty(data, key, {
            enumerable: false,
            configurable: true,
            get() {
                return typeof value === 'object' ? reactive(value) : value
            },
            set(newVal) {
                if (newVal !== value) {
                    fns.forEach(item => item())
                    value = newVal
                }
            }
        })
    })
    return data
}
module.exports = {
    reactive,
    effect
}

vue3基于prxoy实现数据响应式

新版的Vue3使用ES6的Proxy方式来解决这个问题。之前遇到的两个问题就简单的多了。首先Proxy是支持数组的也就是数组是不需要做特别的代码的。对于深层监听也不不必要使用递归的方式解决。当get是判断值为对象时将对象做响应式处理返回就可以了。大家想想这个并不不是发生在初始化的时候而是设置值得时候当然性能上得到很大的提升

let fns = []

function effect(fn) {
    fns.push(fn)
    return () => {
        fns = fns.filter(item !== fn)
    }
}

function reactive(data) {

    if (typeof data !== 'object' || data == null) {
        return data;
    }
    let observed = new Proxy(data, {
        get(target, key, receiver) {
            let res = Reflect.get(target, key, receiver)
            return typeof res === 'object' ? reactive(res) : res;
        },
        set(target, key, value, receiver) {
            let result = Reflect.set(target, key, value, receiver)
            fns.forEach(item => item())
            return result;
        },
        deleteProperty(target, key) {
            let result = Reflect.deleteProperty(target, key)
            return result;
        }
    })
    return observed
}
module.exports = {
    reactive,
    effect
}