Vue双向绑定原理(Observer和Dep)

vue响应式原理的基本流程:
初始化Vue实例时,Observer遍历data里所有属性,使用Object.defineProperty()方法把这些属性都转为getter/setter。并且创建dep管理器(一个属性一个Dep,用来管理该属性下的所有Watcher,如果同一个属性在DOM节点中多次使用会创建多个Watcher)

在解析指令时,创建Watcher,将更新函数放到Watcher的回调上。

初始化视图时,会读取属性值,触发get,将创建的Watcher添加到dep中。

当修改数据时,触发set,调用dep的notify,通知该dep内部所有Watcher的执行回调,重新render当前组件,生成新的虚拟DOM树。

Vue框架会遍历并对比新虚拟DOM树和旧虚拟DOM树种每个节点的差别,并记录下来,最后,加载操作,将所有记录的不同点,局部修改到真DOM树上。

Object.defineProperty()方法大家都知道,用来监测一个对象的值,通过getter和setter属性进行劫持属性的操作和更新

    const data = {
        name: '',
      }
      // 拷贝一下data对象 防止死循环
      let newData = { ...data }
      Object.defineProperty(data, 'name', {
        get: () => {
          console.log('读取')
          return newData.name
        },
        set: (newval) => {
          console.log('设置')
          newData.name = newval
          response()
        },
      })
      setTimeout(() => {
        data.name = 'hello world'
      }, 1000)
      //输入时为data重新赋值 通知监听的方法为newData更改数据来达到页面新数据的动态呈现
      iptName.oninput = function () {
        data.name = this.value
      }
      const response = () => {
        hName.innerText = data.name
        iptName.value = data.name
      }
      
      //简单实现一个Object.defineProperty()
复制代码

创建Observer 递归监控劫持数据

现在开始考虑我们上面的data有多个属性,我们就需要一个方法或者类去遍历这个对象

  //先为Object.defineProperty()封装一个公共的方法 并简化方法里面的操作
 function defineReactive(data, key, value = data[key]) {
   observe(value)//监听子属性
   Object.defineProperty(data, key, {
      get() {
        return value
      },
      set(newValue) {
        value = newValue
      }
   })
 }
创建Observe函数去递归实现数据劫持
const observer =(data) => {
    Object.keys(data).forEach((k) => defineReactive(data, k))
}

const data = {a:1,b:{c:1}}
observer(data)
复制代码

订阅者Dep

接下来我们实现一个消息的订阅器,用来收集订阅者,数据改变的话则触发notify方法,再通过调用订阅者自身的update方法

 function defineReactive(data, key, value = data[key]) {
   var dep = new Dep()
   observe(value)//监听子属性
   Object.defineProperty(data, key, {
      get() {
        return value
      },
      set(newValue) {
        value = newValue
        dep.notify()  //通知所有的订阅者
      }
   })
 }
 function Dep(){
     this.subs = []
 }
 //给Dep的原型上追加add和notify方法
 Dep.prototype = {
    addSub(sub) {
      this.subs.push(sub);
    },
    notify() {
      this.subs.forEach(function(sub) {
        sub.update();
      });
    }
};

//我们在Object.defineProperty的Setter方法中添加了dep.notify(),那么我们就需要在合适的地方通过上面的dep添加订阅者
   //我们选择在getter的时候添加订阅者
   Object.defineProperty(data, key, {
      get() {
        Dep.target&&dep.addSub(Dep.target);
        return value
      },
      set(newValue) {
        value = newValue
        dep.notify()  //通知所有的订阅者
      }
   })

复制代码

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享