vue响应式原理

247 阅读1分钟

vue响应式原理

普通方式对对象的操作无法实现响应式

    //#region 
    // let name='bwf'
    // let age=18
    // let obj1={
    //     name,
    //     age
    // }
    //#endregion
    

vue2响应式原理

存在的问题:

1.增加和删除对象的属性时无法实现响应式,如果需要增加对象属性可以调用set方法,如下

this.$set(this.user,'sex','女')

Vue.set(this.user,'sex','女')

注意:对于已经创建的实例,Vue 不允许动态添加根级别的响应式 property。但是,可以使用 Vue.set(object, propertyName, value) 方法向嵌套对象添加响应式 property

  1. 通过修改数组的索引和length属性无法实现对数组的更新,解决方法如下:
Vue.set(vm.items, indexOfItem, newValue)

m.$set(vm.items, indexOfItem, newValue)

//或者

vm.items.splice(indexOfItem, 1, newValue)

vue2响应式原理的简单实现

 let data = {
            name: 'bwf',
            age: 18
        }



// vue2响应式的简单实现,创建了一个Observer构造函数,用于把传过来的数据加工成响应式的
function Observer(obj) {
    Object.keys(obj).forEach(key => {
    //把obj上的属性复制到this,即构造函数的实例身上
        return Object.defineProperty(this, [key], {
            get() {
                return obj[key]
            },
            set(value) {
                obj[key] = value
                console.log(`有人修改了${key}属性,我要去更新模板了。。。`)
            }
        })
    })
}

const obs = new Observer(data);
const vm = {};
vm._data = data = obs;

console.log('obs', obs);

vue3响应式原理

借助了es6中的Reflect对象与Proxy对象

let obj1 = {
        name: 'bwf',
        age: 18
    }
let obj2 = new Proxy(obj1, {
    // target代理的目标对象,这里指obj1,propKey目标对象的key值
    get(target, propKey) {
        // console.log(target, propKey)
        // return target[propKey]
        return Reflect.get(target, target[propKey])
    },
    // 当修改或新增代理对象的属性时set方法会调用
    set(target, propKey, value) {
        console.log(`有人修改了${target}${propKey}属性`)
        console.log(target, propKey, value)
            // target[propKey] = value

        Reflect.set(target, propKey, value)

    },
    // 当删除对象的某个属性时deleteProperty方法会调用
    deleteProperty(target, propKey) {
        console.log(`有人删除了${target}属性`)
            // delete target[propKey]
        Reflect.deleteProperty(target, propKey)
    }

})

Object.defineProperty 与Reflect.defineProperty的比较

详细请看es6官方文档 es6.ruanyifeng.com/#docs/refle…

Object.defineProperty 定义对象属性若同名会报错,影响后面代码的执行,若不想整个页面报错,需要放在try...catch中

        //#region 
         let num1 = {
           a: 1
        }
        let num2 = {}
        try {
             Object.defineProperty(num2, 'a', {
                get() {
                    return 100
               },
                set(value) {
                    num1.a = 200
                 }

             })
             Object.defineProperty(num2, 'a', {
                 get() {
                    return 300
                },
                set(value) {
                     num1.a = 200
                }

           })

         } catch (error) {
            console.log(error)

         }
         console.log('hah')
       //#endregion

  1. Reflect.defineProperty定义对象属性若同名不会报错,后定义的不会生效
  2. Reflect.defineProperty可以接受一个返回值,为true/false

        //#region 
       let num1 = {
            a: 1
        }
        let num2 = {}

        let res1 = Reflect.defineProperty(num2, 'a', {
            get() {
                return 100
            },
            set(value) {
                num1.a = 200
            }

        })
        console.log(res1)
        let res2 = Reflect.defineProperty(num2, 'a', {
            get() {
                return 300
            },
            set(value) {
                num1.a = 200
            }

        })
        console.log(res2)
        if (res1) {
            console.log('执行成功的代码')

        } else {
            console.log('执行失败的代码')
        }
        console.log('hah')
        //#endregion

so,大型框架中一般使用Reflect来操作对象。