vue2和vue3的双向绑定原理

44 篇文章 0 订阅
43 篇文章 0 订阅
本文深入探讨Vue2.x中的数据劫持,通过Object.defineProperty实现响应式原理,解释如何对data进行劫持以监听属性变化。同时,介绍了Vue3.x中使用Proxy替代Object.defineProperty,解决其局限性,提供更高效的数据监听方式。文中还涉及到深度监听(watch)、数组监听问题及Vue3.x的改进。
摘要由CSDN通过智能技术生成
vue2

首先Vue是个类

new Vue({
    data(){},
    methods: {}
})



实例化Vue的时候要传入data,Vue类内部对data进行劫持转换成getter/setter,如何劫持

vue2.x用的核心函数是Object.defineProperty,defineProperty核心并不是为对象做数据绑定的,而是给对象的属性做一些配置,只不过里面的set和get实现了响应式。defineProperty的缺陷是只能监听一个属性,所以要想监听一个对象,vue2的内部做了一个for in 处理。先写一个vue2.x版本的简单demo

vue2 数据劫持
核心方法: Object.defineProperty(H5方法,所以不兼容IE8以下)

let obj = {},
value = 1
Object.defineProperty(obj,'a',{
    get() {
        console.log('这里监听到了数据获取')
        return value
    },
    set(newValue, value) {
        if(newValue !== value) {
            value = newValue
            console.log('这里监听到了数据更改')
        }
    }
})
console.log(obj.a) // 这里监听到了数据获取   1
obj.a = 2 // 这里监听到了数据更改

所以再初始化Vue时,对data进行了劫持,每个属性都通过Object.defineProperty变成了getter/setter,一旦数据发生改变,就会触发set,然后去更新view

上面的使用中,我们只监听了一个属性的变化,但是在实际情况中,我们通常需要一次监听多个属性的变化。
这时我们需要配合Object.keys(obj)进行遍历。这个方法可以返回obj对象身上的所有可枚举属性组成的字符数组。(其实用for in遍历也可以
下面是该API一个简单的使用效果:

var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '1', '2']


let data = {
    name: 'nike',
    info: {
        age: 21
    }
}
Object.keys(data).forEach(key=>{
    defineProperty(data, key, data[key])
})
function defineProperty(target, key, value) {
    Object.defineProperty(target,key,{
        get() {
            console.log('这里监听到了数据获取')
            return value
        },
        set(newValue, value) {
            if(newValue !== value) {
                value = newValue
                console.log('这里监听到了数据更改')
            }
        }
    })
}
data.name = 'tom' // 这里监听到了数据更改
data.info.age = 22 // 这里监听到了数据获取(这里没有触发更改,get和set相对立,总要触发一个)
data.info = {age:22} // 这里监听到了数据更改

至于data.info.age = 22为什么没有触发set呢,因为上面的逻辑仅仅是对data下面的一层进行了劫持,而再往下的改变是监听不到的,所以就引出了两外一个东西

1.Watch
watch: {
    info: {
        handler(){},
        deep: true
    }
}
此处的deep表示深度监听,这样就会对该属性递归遍历并逐一劫持,类似于深拷贝

2.vue.$set

从字面意思看,就是手动触发set
Object.defineProperty有一个bug,就是无法监听数组(因为数组没key)


let data = {
    name: [],
}
Object.keys(data).forEach(key=>{
    defineProperty(data, key, data[key])
})
function defineProperty(target, key, value) {
    Object.defineProperty(target,key,{
        get() {
            console.log('这里监听到了数据获取')
            return value
        },
        set(newValue, value) {
            if(newValue !== value) {
                value = newValue
                console.log('这里监听到了数据更改')
            }
        }
    })
}
data.name.push('nike') // 这里监听到了数据获取


为了解决这个问题,Vue对数组的方法进行了重写

// 重写push
let oldPush = Array.prototype.push
Array.prototype.push = function() {
    console.log('这里触发view更新')
    oldPush.call(this,...arguments)
}




vue3 数据劫持

很明显,Object.defineProperty有一些缺陷,不仅要遍历data逐个劫持,还不能监听到数组的改变,所以VUE3使用了ES6的Proxy
Proxy字面理解代理,就跟经纪人一样,一旦与某个明星data绑定,那么这个明星想干嘛就得先通过代理

let data = {
    msg: {
        a: 10
    },
    arr: [1, 2, 3]
}
let handler = {
    get(target, key) {
        // 懒监听,去获取的时候才监听对象里面的对象,而不是直接递归循环监听
        console.log('获取key: ' + key)
        if (typeof target[key] === 'object' && target[key] !== null) {
            return new Proxy(target[key], handler)
        }
        return Reflect.get(target, key)
    },
    set(target, key, value) {
        let oldValue = target[key]
        console.log('更新key: ' + key)
        if (oldValue !== value) {
            // 通知view更新
        }
        return Reflect.set(target, key, value)
    }
}
let proxy = new Proxy(data, handler)
proxy.arr.push(4)

 输出结果

 为什么每次都有length,其实Proxy的监听数组实现是把数组变成了一个类数组对象而已

let arr = {
    '0': 1,
    '1': 2,
    length: 2
}

Proxy除了get,set还有deleteProperty/apply/getOwnPropertyDescriptor等等12个方法,恰好与Reflect对应,所以在这些方法里面可以实现拦截器

set(target, key, value) {
    if(key[0] === '_') {
        throw new Error('这是私有变量,不能更改')
    }
    return Reflect.set(target, key, value)
}

Vue2中,双向绑定原理是通过使用Object.defineProperty()方法对数据对象进行劫持。当数据对象的属性被访问或修改时,Vue会通过getter和setter方法来监听数据的变化,并在需要更新视图的时候进行响应式更新。 当使用v-model指令时,Vue2会根据表单元素的类型(如input、textarea、select等)自动为其添加对应的事件监听器,当表单元素的值发生变化时,会触发相应的setter方法,从而更新数据对象的属性值。反过来,当数据对象的属性值发生变化时,Vue会触发getter方法来更新绑定的表单元素的值,实现双向绑定。 而在Vue3中,双向绑定原理有所改变。Vue3使用了Proxy对象来代替Vue2中的Object.defineProperty()方法。Proxy对象可以拦截并自定义对目标对象的操作,包括属性的读取、赋值、删除等操作。通过使用Proxy对象,Vue3能够更直接地监听数据的变化。 在使用新的v-model指令时,Vue3通过监听表单元素的“模型更新”事件来实现双向绑定。当表单元素的值发生变化时,Vue会触发“模型更新”事件,并将新的值传递给数据对象,然后通过Proxy对象拦截并更新数据对象的属性值。反过来,当数据对象的属性值发生变化时,Vue会触发“模型更新”事件,从而更新绑定的表单元素的值。 总而言之,无论是Vue2还是Vue3,双向绑定的本质都是通过数据劫持和事件监听来实现的,只是在具体的实现方式上有所差异。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值