浅析从vue源码看观察者模式(3)


简单一点,即收集完订阅者们的密探 A 只管发布消息,共产党 B 以及更多的共产党只管订阅消息并进行对应的 update 操作,每个模块确保其独立性,实现高内聚低耦合这两大原则。

废话不多说,我们接下来直接开始讲 vue 是如何做的消息封装的

2.1、Dep

Dep,全名 Dependency,从名字我们也能大概看出 Dep 类是用来做依赖收集的,具体怎么收集呢。我们直接看源码

let uid = 0

export default class Dep {
 static target: ?Watcher;
 id: number;
 subs: Array<Watcher>;

 constructor () {
  // 用来给每个订阅者 Watcher 做唯一标识符,防止重复收集
  this.id = uid++
  // 定义subs数组,用来做依赖收集(收集所有的订阅者 Watcher)
  this.subs = []
 }

 // 收集订阅者
 addSub (sub: Watcher) {
  this.subs.push(sub)
 }

 depend () {
  if (Dep.target) {
   Dep.target.addDep(this)
  }
 }

 notify () {
  // stabilize the subscriber list first
  const subs = this.subs.slice()
  for (let i = 0, l = subs.length; i < l; i++) {
   subs[i].update()
  }
 }
}

// the current target watcher being evaluated.
// this is globally unique because there could be only one
// watcher being evaluated at any time.
Dep.target = null

代码很简短,但它做的事情却很重要

  1. 定义subs数组,用来收集订阅者Watcher
  2. 当劫持到数据变更的时候,通知订阅者Watcher进行update操作

源码中,还抛出了两个方法用来操作 Dep.target ,具体如下

// 定义收集目标栈
const targetStack = []

export function pushTarget (_target: Watcher) {
 if (Dep.target) targetStack.push(Dep.target)
 // 改变目标指向
 Dep.target = _target
}

export function popTarget () {
 // 删除当前目标,重算指向
 Dep.target = targetStack.pop()
}

2.2、 Watcher

Watcher 意为观察者,它负责做的事情就是订阅 Dep ,当Dep 发出消息传递(notify)的时候,所以订阅着 Dep 的 Watchers 会进行自己的 update 操作。废话不多说,直接看源码就知道了。

export default class Watcher {
 vm: Component;
 expression: string;
 cb: Function;

 constructor (
  vm: Component,
  expOrFn: string | Function,
  cb: Function,
  options?: Object
 ) {
  this.vm = vm
  vm._watchers.push(this)
  this.cb = cb
  // parse expression for getter
  if (typeof expOrFn === 'function') {
   this.getter = expOrFn
  } else {
   // 解析表达式
   this.getter = parsePath(expOrFn)
   if (!this.getter) {
    this.getter = function () {}
   }
  }
  this.value = this.get()
 }

 get () {
  // 将目标收集到目标栈
  pushTarget(this)
  const vm = this.vm
  
  let value = this.getter.call(vm, vm)
  // 删除目标
  popTarget()
  
  return value
 }

 // 订阅 Dep,同时让 Dep 知道自己订阅着它
 addDep (dep: Dep) {
  const id = dep.id
  if (!this.newDepIds.has(id)) {
   this.newDepIds.add(id)
   this.newDeps.push(dep)
   if (!this.depIds.has(id)) {
    // 收集订阅者
    dep.addSub(this)
   }
  }
 }

 // 订阅者'消费'动作,当接收到变更时则会执行
 update () {
  this.run()
 }

 run () {
  const value = this.get()
  const oldValue = this.value
  this.value = value
  this.cb.call(this.vm, value, oldValue)
 }
}


      

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/458.html