Vue源码观察者模式的应用

观察者模式的主要实现是通过发布者与订阅者来实现消息的便捷传递。

在Vue源码中,我观察到主要由以下三个步骤来实现:

1.对每个对象进行数据绑定

defineReactive

 1  1 function defineReactive (
 2  2   obj,
 3  3   key,
 4  4   val,
 5  5   customSetter,
 6  6   shallow
 7  7 ) {
 8  8    //新建一个发布者对象进行注册
 9  9   var dep = new Dep();
10 10    //获取属性进行绑定
11 11   var property = Object.getOwnPropertyDescriptor(obj, key);
12 12   if (property && property.configurable === false) {
13 13     return
14 14   }
15 15 
16 16   // 适应预先定义的get set方法
17 17   var getter = property && property.get;
18 18   var setter = property && property.set;
19 19   if ((!getter || setter) && arguments.length === 2) {
20 20     val = obj[key];
21 21   }
22 22 
23 23   var childOb = !shallow && observe(val);
24 24   Object.defineProperty(obj, key, {
25 25     enumerable: true,
26 26     configurable: true,
27 27     //对getter方法进行劫持
28 28     get: function reactiveGetter () {
29 29       var value = getter ? getter.call(obj) : val;
30 30       if (Dep.target) {
31 31         dep.depend();
32 32         if (childOb) {
33 33           childOb.dep.depend();
34 34           if (Array.isArray(value)) {
35 35             dependArray(value);
36 36           }
37 37         }
38 38       }
39 39         return value
40 40     },
41 41     //数据变更后才能察觉到
42 42     set: function reactiveSetter (newVal) {
43 43       var value = getter ? getter.call(obj) : val;
44 44       /* eslint-disable no-self-compare */
45 45       if (newVal === value || (newVal !== newVal && value !== value)) {
46 46         return
47 47       }
48 48       /* eslint-enable no-self-compare */
49 49       if (process.env.NODE_ENV !== 'production' && customSetter) {
50 50         customSetter();
51 51       }
52 52       if (setter) {
53 53         setter.call(obj, newVal);
54 54       } else {
55 55         val = newVal;
56 56       }
57 57       childOb = !shallow && observe(newVal);
58 58        //发布者进行通知     
59 59       dep.notify();
60 60     }
61 61   });
62 62 }    

2.发布者

Dep :作用主要有两个,① 添加移除订阅者 ② 发布消息到每一个订阅者

 1 export default class Dep {
 2   static target: ?Watcher;
 3   id: number;
 4   subs: Array<Watcher>;
 5 
 6   constructor () {
 7     //为每个观察者分配一个id作为标示
 8     this.id = uid++
 9     this.subs = []
10   }
11   //添加观察者  
12   addSub (sub: Watcher) {
13     this.subs.push(sub)
14   }
15    //移除观察者 
16   removeSub (sub: Watcher) {
17     remove(this.subs, sub)
18   }
19 
20   depend () {
21     if (Dep.target) {
22       Dep.target.addDep(this)
23     }
24   }
25 
26   notify () {
27     // 首先对观察者列表进行处理
28     const subs = this.subs.slice()
29     if (process.env.NODE_ENV !== 'production' && !config.async) {
30       // subs aren't sorted in scheduler if not running async
31       // we need to sort them now to make sure they fire in correct
32       // order
33       subs.sort((a, b) => a.id - b.id)
34     }
35     //把消息的变更通知到每一个订阅者
36     for (let i = 0, l = subs.length; i < l; i++) {
37       subs[i].update()
38     }
39   }
40 }
41 
42 // the current target watcher being evaluated.
43 // this is globally unique because there could be only one
44 // watcher being evaluated at any time.
45 Dep.target = null
46 const targetStack = []
47 
48 export function pushTarget (_target: ?Watcher) {
49   if (Dep.target) targetStack.push(Dep.target)
50   Dep.target = _target
51 }
52 
53 export function popTarget () {
54   Dep.target = targetStack.pop()
55 }

3.观察者

Watcher  主要作用:① 订阅Dep ② 收到notify通知时,进行update操作

 1 export default class Watcher {
 2   vm: Component;
 3   expression: string;
 4   cb: Function;
 5 
 6   constructor (
 7     vm: Component,
 8     expOrFn: string | Function,
 9     cb: Function,
10     options?: Object
11   ) {
12     this.vm = vm
13     vm._watchers.push(this)
14     this.cb = cb
15     // parse expression for getter
16     if (typeof expOrFn === 'function') {
17       this.getter = expOrFn
18     } else {
19       // 解析表达式
20       this.getter = parsePath(expOrFn)
21       if (!this.getter) {
22         this.getter = function () {}
23       }
24     }
25     this.value = this.get()
26   }
27 
28   get () {
29     // 将目标收集到目标栈
30     pushTarget(this)
31     const vm = this.vm
32 
33     let value = this.getter.call(vm, vm)
34     // 删除目标
35     popTarget()
36 
37     return value
38   }
39 
40   // 订阅 Dep,同时让 Dep 知道自己订阅着它
41   addDep (dep: Dep) {
42     const id = dep.id
43     if (!this.newDepIds.has(id)) {
44       this.newDepIds.add(id)
45       this.newDeps.push(dep)
46       if (!this.depIds.has(id)) {
47         // 收集订阅者
48         dep.addSub(this)
49       }
50     }
51   }
52 
53   // 订阅者'消费'动作,当接收到变更时则会执行
54   update () {
55     this.run()
56   }
57 
58   run () {
59     const value = this.get()
60     const oldValue = this.value
61     this.value = value
62     this.cb.call(this.vm, value, oldValue)
63   }
64 }

总结:

本次对vue源码的阅读,让我对观察者模式有了进一步的认识,目的是为了实现一对多依赖关系的解耦。在发布者这本身状态改变时能够发布通知使得每个订阅者能够自己去做出相应的改变。

猜你喜欢

转载自www.cnblogs.com/youfei/p/9846221.html