【Vue高级知识】如何使用Vue劫持对象或数组的属性

本文深入探讨Vue的响应式原理,通过Object.defineProperty实现数据劫持,指出在更新对象属性时需要递归劫持子对象,以及在处理数组时必须重写其方法以监听变化。在实际使用中,应注意新绑定属性需使用$set方法,并且数组变更应依赖数组方法,而非直接修改length等属性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Vue创建一个实例

    <div id="app">{{msg}}</div>
        <script src="node_modules/vue/dist/vue.js"></script>
        <script>
        let vm = new Vue({
            el:'#app',
            // template加上之后会替换掉#app这个标签
            // template:'<h1>en</h1>',
            data:{msg:'msg'}
        })
        vm.msg = 'msg'
        </script>

vm的实质

当vm中的数据改变之后,视图也会发生变化,这就是vue的数据驱动视图,同时也是vue的响应式的实现原理,vue是通过Object.defineProperty来实现的响应式的,也就是对data中的属性的读取和设置操作都进行了监听,当有这样的操作的时候(get或者set的时候),就会执行相关的方法。

    // 对obj上面的属性进行读取和设置监听
    let obj = {
        name:'huahua',
        age:18
    }
    function observer(obj){
    	// 这里相当于是一个观察者,只针对js对象进行相关的操作
        if(typeof obj === 'object'){
            for (const key in obj) {
            	// 开始对obj这个对象进行监听
                defineReactive(obj,key,obj[key])
            }
        }
    }
    // get的return的值才是最终你读取到的值。所以设的值是为读取准备的。
    // set传的参数是设置的值,注意这里不要有obj.name = newVal 这样又触发set监听,会死循环的。
    function defineReactive(obj,key,value){
        Object.defineProperty(obj,key,{
            get:function(){
                console.log('你在读取')
                // happy的话这边可以value++,这样你发现读取的值始终比设置的大一个,因为return就是读取到的值
                return value
            },
            set:function(newVal){
                console.log('数据更新了')
                value = newVal
            }

        })
    }
    observer(obj)
    obj.age = 2					// 触发set方法
    console.log(obj.age)		// 触发get方法

在浏览器执行的时候,控制台随手也可以obj.name="hua1"类似的操作,发现都监听到了。但是如果更深一步,

 		obj.name={firstname:'hua',lastname:'piaoliang'};
		obj.name.lastname='o';

就不能监听到属性修改了。因为并没有将新的赋值对象监听其属性。所以函数需要改进。 需要在defineReactive的第一行加上observer(value)。设置值的时候如果是对象的话,也需要将这个对象数据劫持。同理,set那边也需要加这行。

    function defineReactive(obj,key,value){
       // 注意这里!!!!!!!
        observer(value)
        Object.defineProperty(obj,key,{
            get:function(){
                return value
            },
            set:function(newVal){
                // 注意这里!!!!!!!
                observer(newVal)
                console.log('数据更新了')
                value = newVal
            }

        })
    }

如果

	// 给obj对象添加一个name属性,这个属性的值是一个数组
	obj.name=[1,2,3];
	// 开始调用数组的push方法,向数组里面添加数据
	obj.name.push(4);

测试发现又没有通知了,这是因为Object.defineProperty不支持监听数组变化。所以需要重写数组上面的方法。如何解决呢?可以通过重写数组的方法来实现数据监听

   // 把数组上大部分方法重写了,这里不一一列举。但是如果你 [1,2].length--,这是捕捉不到的
        let arr = ['push','slice','split']
        arr.forEach(method=>{
        	// Array中内置的方法
            let oldPush = Array.prototype[method]// 开始重写Array内置的方法(直接修改原型中的属性和方法)
            Array.prototype[method] = function(value){
                console.log('数据更新')// 当数据更新之后,通过call再次回调this中的方法,并传入参数value
                oldPush.call(this, value)}
        })

vue使用的时候注意的坑

vue2.0的使用需要注意的点(总结):

因为是一开始就数据劫持了。所以后来如果新绑定属性,是没有数据劫持的。如果需要调用 vm.$set(vm.info,‘newKey’,‘newValue’),vm是vue的实例。

当属性值是数组,数组变化的时候,跟踪不到变化。因为数组虽然是对象,但是Object.defineProperty不支持数组,所以vue改写了数组的所有方法,当调用数组方法的时候,就调动变动事件。但是不能通过属性或者索引控制数组,比如length,index。

总结:data上,绑定所有属性避免后期加新属性。如果是数组,只能通过数组方法修改数组。如下例子,控制台vm.arr.length发现视图并不会变化,vm.arr.push(4)就能变化

    <div id="app">{{msg}}{{arr}}</div>
        <script src="node_modules/vue/dist/vue.js"></script>
        <script>
        let vm = new Vue({
            el:'#app',
            // template加上之后会替换掉#app这个标签
            // template:'<h1>en</h1>',
            data:{msg:'msg',arr:[1,2,3]}
        })
        vm.msg = 'msg';
        vue.arr.push(2019);	// ok
        vue.arr.length = 2; // error
        </script>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值