44.手写v2响应式原理

67 阅读2分钟

之前更新的时候讲过vue的响应式原理,今天来写一些重要的内容.

订阅者 Dep

订阅者dep是用来收集依赖和通知依赖更新的类,每一个相应式数据都有一个对应的dep实例,他有方法dep.add将依赖收集进dep数组,当数据发送改变时可以通过dep.notify去通知依赖更新.

    class Dep {
      constructor () {
        this.deps = []; // 用来存储全部的依赖watcher
      }
      // 收集依赖
      addDep = (watcher) => {
        this.deps.push(watcher);
        console.log('收集依赖的列表', this.deps);
      };
      // 通知依赖
      notify = (params) => {
        console.log('params', params);
        this.deps.forEach((item) => {
          item.update(params); //  通过watch中的update,通知页面更新
        });
      };
    }

发布者 Observe

Observe类有个方法就是definereactive函数,遍历递归data数据,给每一个属性添加getter和setter属性,使其变成响应式数据

 class Observer {
      constructor (data) {
        this.walk(data)
      }
      // 1. 判断数据是否是对象,如果不是对象返回 
      // 2. 如果是对象,遍历对象的所有属性,设置为 getter/setter
      walk (data) {
        if (!data || typeof data != 'object') {
          return
        }
        Object.keys(data).forEach(key => {
          this.defineReactive(data, key, data[key])
        })
      }

      //  定义响应式成员  即对data总的数据实现setter和getter
      defineReactive (data, key, val) {
        const that = this
        // 如果 val 是对象,继续设置它下面的成员为响应式数据 

        // this.walk(val)
        Object.defineProperty(obj, 'age', {
          get () {
            console.log('watcher', watcher);
            dep.addDep(watcher); // 收集依赖
            return val; // return的值不能直接object.age,会溢出
          },
          set (val) {
            console.log('set调取');
            dep.notify(val);
          }
        });
      }
    }

观察者 Watcher

Vue 中定义一个 Watcher 类来表示观察订阅依赖。我们在响应式数据可能在很多地方用到,比如模板,或者一个watch,那么我们每一次用到的地方就要生成这个数据的一个watcher的实例记录相关的依赖,当数据改变的时候我们通知这些watcher去更新视图.

    //属性全部的依赖就是watcher
    class Watcher {
      constructor (id) {
        this.id = id;
      }
      update = (value) => {
        console.log('数据改变了,通知我要更新', value);
        document.getElementById('text').innerText = value;
      }
    }

测试

    let obj = {
      name: 'nihao',
      age: 18
    };
    let watcher = new Watcher('1'); // 实例化一个watcher
    let dep = new Dep(); //实例化一个dep
    new Observer(obj)
    obj.age = 23; // 改变age,会调取set方法


    document.getElementById('app').addEventListener('click', () => {
      obj.age = (Math.random() * 20).toFixed();
    });

image.png image.png