Vue2源码分析-day2

实现数组的响应式原理

首先我们在index.html中定义一个数组,并且打印实例

const vm = new MVue({
    data() {
        return {
            name: "zhangsan",
            age: "16",
            hobby:['zhangsan','lisi']
        }
    }
})
console.log(vm);

我们会发现定义的数组每一项都有get和set方法虽然数组是被劫持了但是这样做性能非常差。如果说数组元素有1000个或者10000个能?那么我们要循环劫持1000次或者10000次吗?这显然是不合理的,所以我们对数组要单独做数据劫持。

在这里插入图片描述
数组一般用户都是push,pop这所方法去操作数组元素所以,我们要重写这些方法。

首先我们先判断是否为数组,obseve/index.js文件下Observer类修改如下代码

constructor(data) {
    /* 
        这里注意,Object.defineProperty只能劫持已经存在的属性,新增或删除的无法劫持
        这也是为什么vue2中新增了,$set和$delete方法这类方法的原因
    */

    if(Array.isArray(data)){
	
        // 重写原型对象上的方法
      	data.__proto__=new_array_proto
        // 劫持数组
        this.observe_array(data)
    }else{
        // 劫持对象
        this.walk(data)
    }

}
walk(data) {
    // 循环对象keys依次劫持,重新定义属性
    Object.keys(data).forEach(key => {
        define_reactive(data, key, data[key])
    })
}
observe_array(data){
    // 如果数组中包含对象,则走对象劫持
    data.forEach((item)=>observe(item))
}

observe/index.js文件下创建array.js文件用来重写数组方法。并且导出new_array_proto


// 获取原数组原型对象
const old_arr_proto=Array.prototype;

// 拷贝新原型对象
export let new_array_proto=Object.create(old_arr_proto);

// 定义所以变异方法
let methods=['push','pop','shift','unshift','reverse','sort','splice'];

methods.forEach((item)=>{   
    new_array_proto[item]=function(...args){

        console.log(123);
        // 调用原原型上的方法
        let res=old_arr_proto[item].call(this,...args);

        return res
    }
})

然后我们在index.html调用push方法看看控制台是否打印123。如果打印就说明重写成功

在这里插入图片描述

下面我们来对我们的变异方法做进一步处理。因为新增的属性并没有被劫持


// 获取原数组原型对象
const old_arr_proto=Array.prototype;

// 拷贝新原型对象
export let new_array_proto=Object.create(old_arr_proto);

// 定义所以变异方法
let methods=['push','pop','shift','unshift','reverse','sort','splice'];

methods.forEach((item)=>{   
    new_array_proto[item]=function(...args){

       
        // 调用原原型上的方法
        let res=old_arr_proto[item].call(this,...args);

        //对新增的属性进行观测
        let val;
        switch(item){
            case "push":
            case "unshift":
                val=args;
                break;
            case "splice":
                val=args.slice(2);
                break;
        }

        // this为当前变异方法的调用者。_ob_为观测类的实例
        if(val){
            this._ob_.observe_array(val)		// 可能到这里你会好奇这个_ob_是哪里来的。别急下面我将解释
        }

        return res
    }
})

observe/index.js文件下中构造函数增加如下代码。添加_ob_一方面是为了给已经观测的数据打上标识,另一方便是为了调用数组的变异方法时如果新增的是对象那么,通过_ob_获取到观测类实例对这个新增对象进行观测

    constructor(data) {
        /* 
            这里注意,Object.defineProperty只能劫持已经存在的属性,新增或删除的无法劫持
            这也是为什么vue2中新增了,$set和$delete方法这类方法的原因
        */

        // 对即将观测的数据添加_ob_属性。添加被观测标识
        Object.defineProperty(data, '_ob_', {
            value:this,
            enumerable:false    // 禁止枚举,防止死循环
        })

        if(Array.isArray(data))
        {
            // 重写原型对象上的方法
            data.__proto__=new_array_proto
            
            
            // 劫持数组
            this.observe_array(data)
        }else{
            // 劫持对象
            this.walk(data)
        }

    }

既然已经被观测的数据打上了表示,那么为了防止重复观测我们就可以在observe方法中添加如下代码

 // 对对象劫持
 if (typeof data !== 'object' || data === null) {
     return;
 }

 // 如果已经被检测过直接返回
 if(data._ob_ instanceof Observer){
     return data._ob_
 }

 // 如果一个对象被劫持过就不需要再次劫持,我们可以通过一个实例判断是否被劫持过
 return new Observer(data)

下面我们在index.html文件下push一个对象就可以看到这个对象也被观测到了

在这里插入图片描述文章来源地址https://uudwc.com/A/jVm68

原文地址:https://blog.csdn.net/m0_60716245/article/details/132178477

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请联系站长进行投诉反馈,一经查实,立即删除!

h
上一篇 2023年08月09日 05:08
Pandaer的iPhone手机壳
下一篇 2023年08月09日 05:08