Vue响应式原理02-实现Observer劫持并监听所有属性

582 阅读1分钟

先来看下目录结构:

xvue原理学习        
├─ MVue.js     
├─ Observe.js  
├─ index.html     

1.Observe.js中的劫持监听

先来看看Mvue.js中对Observe类的使用:

  • 通过new Observer(this.$data)将数据传递给Observer
//①入口类MVue
class MVue {
    constructor(options) {
        this.$el = options.el;
        this.$data = options.data;
        this.$options = options;
        //如果有这个$el根节点
        if (this.$el) {
            //🌵实现一个数据观察者
            new Observer(this.$data)
            //实现一个指令解析器
            new Compile(this.$el, this)
        }
    }
}

然后再来看下如何完成对数据的劫持和监听:

  • 创建Observe类,constructor中传入想要监听的数据data

  • observe方法中对data进行判断,然后forEach遍历,对每个item数据子项调用defineReactive方法从而完成对data数据的劫持。

    • Object.keys()遍历对象的属性名key,返回一个属性名字组成的数组

    • Object.defineProperty():developer.mozilla.org/zh-CN/docs/…

    • 问题:如果直接通过vm.$data.person = {a;1}去修改了person对象,那么新修改的person对象不会被监听。

    • 解决办法Object.defineProperty()set方法,需要调用this.observe(newVal),这样如果vm.$data.person对象被重写,也会被监听。

  • defineReactive方法写完之后可以在浏览器控制台中输入vm.$data,然后改变vm.$dataperson对象的值,看看是否会出现setget方法。

//Observe.js
        // data中可能的数据结构
        // {
        //     person: {
        //         name: "悟空",
        //         age: 18,
        //         fav: '姑娘'
        //     },
        // }

class Observer{
    constructor(data){
        //①🌵
        this.observe(data)
    }
    //②🌵
    observe(data){
        if(data && typeof data === "object")
        {
            console.log("我是所有的keys",Object.keys(data));//["person", "msg", "htmlStr"]
            Object.keys(data).forEach((key)=>{
                //劫持监听方法
                this.defineReactive(data,key,data[key]);
            })
        }
    }
    //③🌵和这个方法写完之后可以在浏览器控制台中输入vm.$data,然后改变vm.$data中对象的值
    defineReactive(obj,key,value){
        //看看数据是否有多层,有的话,需要递归遍历
        this.observe(value);
        Object.defineProperty(obj,key,{
            enumerable:true,
            configurable:false,
            get(){
                //订阅数据变化时,往Dep中添加观察者(收集每个观察者的依赖)
                return value;
            },
            set:(newVal)=>{
//如果vm.$data.person对象被重写,那么就不会被监听,所以这里通过箭头函数来再次调用observe方法(箭头函数来归正this指向问题),让新值也被监听
                this.observe(newVal);
                if(newVal !== value)
                {
                    value = newVal
                }
            }
        })
    }
}

下篇将继续完成对劫持的数据进行更新,以及Watcher类...