# Vue中的设计模式

# 观察者模式/发布订阅模式

观察者模式属于行为型模式,将不同行为代码解耦。

观察者模式(Observer):通常又被称作为发布-订阅者模式。

它定义了一种一对多的依赖关系,即当一个目标对象(被观察者Observable)的状态发生改变的时候,所有依赖于它的对象(观察者Observer)都会得到通知并自动更新,解决了主体对象与观察者之间功能的耦合。

一般情况下,被依赖的对象叫作被观察者(Observable),依赖的对象叫做观察者(Observer).

# vue中的使用

Vue中有三个类

  • Dep 被观察者: dep.depend()收集依赖,也就是收集watcher,触发更新dep.notify();
  • Watcher 观察者:订阅Dep类,能接受Dep类发出的数据更新通知,当收到通知的时候调用 watcher.update
  • Observer 在对数据进行 Object.defineProperty 定义dep,当渲染组件的时候触发数据的getter时将Dep和Watcher进行关联操作

观察执行逻辑

  1. new Vue()
  2. this._init()
  3. initState
  4. initData
  5. observe
  6. Observer
  7. walk
  8. defineReactive$$1

组件开始挂载

  1. mountComponent
  2. new Watcher();
  3. this.get()
  4. this.getter.call(vm, vm)
  5. vm._update(vm._render(), hydrating);
  6. render.call(vm._renderProxy, vm.$createElement)
  7. (function anonymous() {with(this){return _c('div',{attrs:{"id":"app"}},[_v(_s(name))])}})
  8. 触发this.name,
  9. 触发getter也就是 reactiveGetter ()
  10. 执行dep.depend()
  11. 执行 dep.depend();
  12. 执行 Dep.target.addDep(this), Dep.target 此时是watcher实例, this是dep的实例
  13. 执行 this.newDepIds.add(id); // this就是watcher的实例
  14. 执行 this.newDeps.push(dep);
  15. dep.addSub(this); // this是watcher的实例

数据更新触发组件更新

  1. this.name = 2
  2. 触发了setter函数
  3. dep.notify()
  4. subs[i].update(); // subs 每一项都是vue实例对应的唯一的watcher
  5. 那么也就是触发了watcher.update
  6. 执行queueWatcher(this); this就是watcher的实例
<div id="app">{{name}}</div>

var vm = new Vue({
    el:'#app',
    data() {
        return {
            name: '1'
        }
    },
    created() {
        setTimeout(()=>{
            this.name = 2
        },3000)
    },
})

/** initState 
 * 该过程执行之后data的name 变成了
 * {
 *      name: 1,
 *      __ob__: new Observer()
 * }
 * 
 */
function initState (vm) {
    vm._watchers = [];
    initData(vm)
}
function initData (vm){
    // data = {name:1}
    observe(data, true /* asRootData */);
}
function observe(value, asRootData) {
    // value = {name:1}
    ob = new Observer(value);
    return ob
}



// >>>>>>>>>>>>>   Observer 定义>>>>>>>>>>>>>>>>>>
function Observer (value){
    this.value = value;
    this.dep = new Dep();
    // value = {name:1,__ob__: new Observer() }
    def(value, '__ob__', this);
    this.walk(value);
}
Observer.prototype.walk = function walk (obj) {
    // obj = {name:1,__ob__: new Observer() }
    var keys = Object.keys(obj); // [name]
    for (var i = 0; i < keys.length; i++) {
     // defineReactive(obj, 'name')
      defineReactive(obj, keys[i]);
    }
};

function defineReactive (obj,key, val, customSetter,shallow ) {
// obj = {name:1,__ob__: new Observer() }
// key = name
// val customSetter shallow 都是undefined


    // 定义被观测者,等触发getter的时候做依赖收集
    var dep = new Dep();
    val = obj[key]; // obj[name] = 1
    var childOb = !shallow && observe(val);
    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter () {
          var value = getter ? getter.call(obj) : val;
          if (Dep.target) {
            dep.depend();
          }
          return value
        },
        set: function reactiveSetter (newVal) {
          var value = getter ? getter.call(obj) : val;
          val = newVal;
          dep.notify();
        }
    });
}



// >>>>>>>>>>>>>   Dep 定义>>>>>>>>>>>>>>>>>>
  var Dep = function Dep () {
    this.id = uid++;
    this.subs = [];
  };
  Dep.prototype.addSub = function addSub (sub) {
    this.subs.push(sub);
  };
  Dep.prototype.depend = function depend () {
    if (Dep.target) {
      Dep.target.addDep(this);
    }
  };
  Dep.prototype.notify = function notify () {
    var subs = this.subs.slice();
    for (var i = 0, l = subs.length; i < l; i++) {
      subs[i].update();
    }
  };


// >>>>>>>>>>>>>   Watcher 定义>>>>>>>>>>>>>>>>>>

var Watcher = function Watcher (vm,expOrFn,cb,options,isRenderWatcher) {
    this.vm = vm;
    vm._watcher = this;
    vm._watchers.push(this);
    this.before = options.before;
    this.cb = cb;
    this.deps = [];
    this.newDeps = [];
    this.depIds = new _Set();
    this.newDepIds = new _Set();
    this.expression =  expOrFn.toString()
    // expOrFn = function () {
    //     vm._update(vm._render(), hydrating);
    // };
    this.getter = expOrFn;
    this.value = this.lazy
      ? undefined
      : this.get();
}
Watcher.prototype.get = function get () {
    pushTarget(this);// Dep.target = this
    var value;
    var vm = this.vm;
    value = this.getter.call(vm, vm);
    popTarget();
    this.cleanupDeps();
    return value
};
Watcher.prototype.addDep = function addDep (dep) {
    var id = dep.id;
    if (!this.newDepIds.has(id)) {
      this.newDepIds.add(id);
      this.newDeps.push(dep);
      if (!this.depIds.has(id)) {
        dep.addSub(this);
      }
    }
};
Watcher.prototype.update = function update () {
    queueWatcher(this);
};


// 组件挂载
function mountComponent (){
    callHook(vm, 'beforeMount');
    var updateComponent;
    updateComponent = function () {
        vm._update(vm._render(), hydrating);
    };
    new Watcher(vm, updateComponent, noop, {
        before: function before () {
          if (vm._isMounted && !vm._isDestroyed) {
            callHook(vm, 'beforeUpdate');
          }
        }
    }, true);
    callHook(vm, 'mounted');
    return vm
}
   
var Watcher = function Watcher (
    vm,
    expOrFn,
    cb,
    options,
    isRenderWatcher
  ) {
    this.vm = vm;
    vm._watcher = this;
    vm._watchers.push(this);
    this.before = options.before;
    this.cb = cb;
    this.deps = [];
    this.newDeps = [];
    this.depIds = new _Set();
    this.newDepIds = new _Set();
    this.expression =  expOrFn.toString()
    // expOrFn = function () {
    //     vm._update(vm._render(), hydrating);
    // };
    this.getter = expOrFn;
    this.value = this.lazy
      ? undefined
      : this.get();
}

Watcher.prototype.get = function get () {
    pushTarget(this);
    var value;
    var vm = this.vm;
    value = this.getter.call(vm, vm);
    // 执行 vm._update(vm._render(), hydrating);
    popTarget();
    this.cleanupDeps();
    return value
};





var MAX_UPDATE_COUNT = 100;
var callbacks = [];
var queue = [];
var activatedChildren = [];
var has = {};
var circular = {};
var waiting = false;
var flushing = false;
var index = 0;

function queueWatcher(watcher) {
    var id = watcher.id;
    queue.push(watcher);
    nextTick(flushSchedulerQueue);
}

function nextTick (cb, ctx) {
    callbacks.push(function () {
        cb.call(ctx);
    });
    if (!pending) {
      pending = true;
      timerFunc();
    }
    // $flow-disable-line
    if (!cb && typeof Promise !== 'undefined') {
      return new Promise(function (resolve) {
        _resolve = resolve;
      })
    }
  }

  timerFunc = function () {
    p.then(flushCallbacks);
  }

  function flushCallbacks () {
    pending = false;
    var copies = callbacks.slice(0);
    callbacks.length = 0;
    for (var i = 0; i < copies.length; i++) {
      copies[i](); // 也就是 flushSchedulerQueue
    }
  }


  function flushSchedulerQueue () {
    currentFlushTimestamp = getNow();
    flushing = true;
    var watcher, id;

    // Sort queue before flush.
    // This ensures that:
    // 1. Components are updated from parent to child. (because parent is always
    //    created before the child)
    // 2. A component's user watchers are run before its render watcher (because
    //    user watchers are created before the render watcher)
    // 3. If a component is destroyed during a parent component's watcher run,
    //    its watchers can be skipped.
    queue.sort(function (a, b) { return a.id - b.id; });

    // do not cache length because more watchers might be pushed
    // as we run existing watchers
    for (index = 0; index < queue.length; index++) {
      watcher = queue[index];
      if (watcher.before) {
        watcher.before(); // 执行beforeUpdate钩子
      }
      id = watcher.id;
      has[id] = null;
    //   
      watcher.run();
      // in dev build, check and stop circular updates.
    }

    // keep copies of post queues before resetting state
    var activatedQueue = activatedChildren.slice();
    var updatedQueue = queue.slice();

    resetSchedulerState();

    // call component updated and activated hooks
    callActivatedHooks(activatedQueue);
    callUpdatedHooks(updatedQueue);

    // devtool hook
    /* istanbul ignore if */
    if (devtools && config.devtools) {
      devtools.emit('flush');
    }
  }


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305