vue响应式原理

一、观察者模式

  • 观察者(订阅者) -- Watcher
    update():当事件发生时,具体要做的事情
  • 目标(发布者) -- Dep
    subs 数组:存储所有的观察者
    addSub():添加观察者
    notify():当事件发生,调用所有观察者的 update() 方法
  • 没有事件中心
// 目标(发布者)
// Dependency
class Dep {
  constructor() {
    // 存储所有的观察者
    this.subs = []
  }
  // 添加观察者
  addSub(sub) {
    if (sub && sub.update) {
      this.subs.push(sub)
    }
  }
  // 通知所有观察者
  notify() {
    this.subs.forEach(sub => {
      sub.update()
    })
  }
}
// 观察者(订阅者)
class Watcher {
  update() {
    console.log('update')
  }
}
// 测试
let dep = new Dep()
let watcher = new Watcher()
dep.addSub(watcher)
dep.notify()
  • 总结
    观察者模式是由具体目标调度,比如当事件触发,Dep 就会去调用观察者的方法,所以观察者模式的订阅者与发布者之间是存在依赖的。
    发布/订阅模式由统一调度中心调用,因此发布者和订阅者不需要知道对方的存在。

二、响应式原理

  1. watcher在第一次视图绑定变量的时候(插值表达式、指令处理时)创建,每个key有自己的一个watcher。
  2. 创建watcher的时候应该加到对应的发布者里去,所以在watcher构造函数处调用dep.addSub(watcher)
  3. 发布者当数据变更时需要通知观察者dep.notify(),所以发布者的实例创建即new Dep()应该是在数据变更处,即Observer的walk里。
  4. 而watcher构造函数里为了能调用到Observer的walk里defineProperty的dep实例,可以借用this.oldValue = vm[key]去操作。所以可以把dep.addSub(watcher)写在getter里,并通过Dep.target控制只有第一次才addSub。
// 处理插值表达式
  compileText(node) {
    const regExp = /\{\{(.+?)\}\}/
    const value = node.textContent
    if (regExp.test(value)) {
      const key = RegExp.$1.trim()
      node.textContent = value.replace(regExp, this.vm[key])

      // 为当前的key创建watcher,当key变化时更新视图
      new Watcher(this.vm, key, (newValue) => {
        node.textContent = newValue
      })
    }
  }
class Watcher {
  // 每个数据变化都要触发更新,所以每个key对应一个watcher
  constructor(vm, key, cb) {
    this.vm = vm
    this.key = key
    this.cb = cb
    
    // 创建watcher的时候应该加到对应的发布者里去,发布者当数据变更时需要通知观察者,所以发布者应该是在数据变更处,那么添加watcher也可以放在对应地方即Observer的walk里
    // 此处跟Observer里walk相关的也就获取旧数据,为了在walk里面触发添加watcher,可以把添加写在getter里,并通过Dep.target控制只有第一次才addSub

    // 把watcher对象记录到Dep类的静态属性target
    Dep.target = this
    // 触发get方法,在get方法中会调用addSub
    this.oldValue = vm[key]
    Dep.target = null
  }

  // 当数据变化的时候更新视图
  update() {
    let newValue = this.vm[this.key]
    if (this.oldValue === newValue) {
      return
    }

    this.cb(newValue)
  }
}
class Observer {
  constructor(data) {
    this.walk(data)
  }

  walk(data) {
    if (!data || typeof data !== 'object') {
      return
    }
    
    Object.keys(data).forEach(key => {
      const that = this
      const val = data[key] // 避免循环先存下来
      const dep = new Dep() // 负责收集依赖,当数据变化的时候通知观察者
      this.walk(val)
      Object.defineProperty(data, key, {
        enumerable: true,
        configurable: true,
        get() {
          // 只有创建watcher时Dep.target才存在,所以只有创建watcher的时候获取数据才会触发添加watcher
          Dep.target && dep.addSub(Dep.target)

          return val
        },
        set(newVal) {
          if (newVal === val) {
            return
          }
          that.walk(newVal)
          val = newVal // val被闭包缓存了,此处修改的是当前作用域下的val,get返回的也是同一个val
          dep.notify() // 通知观察者调用update
        }
      })
    })
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容