# 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进行关联操作
观察执行逻辑
- new Vue()
- this._init()
- initState
- initData
- observe
- Observer
- walk
- defineReactive$$1
组件开始挂载
- mountComponent
- new Watcher();
- this.get()
- this.getter.call(vm, vm)
- vm._update(vm._render(), hydrating);
- render.call(vm._renderProxy, vm.$createElement)
- (function anonymous() {with(this){return _c('div',{attrs:{"id":"app"}},[_v(_s(name))])}})
- 触发this.name,
- 触发getter也就是 reactiveGetter ()
- 执行dep.depend()
- 执行 dep.depend();
- 执行 Dep.target.addDep(this), Dep.target 此时是watcher实例, this是dep的实例
- 执行 this.newDepIds.add(id); // this就是watcher的实例
- 执行 this.newDeps.push(dep);
- dep.addSub(this); // this是watcher的实例
数据更新触发组件更新
- this.name = 2
- 触发了setter函数
- dep.notify()
- subs[i].update(); // subs 每一项都是vue实例对应的唯一的watcher
- 那么也就是触发了watcher.update
- 执行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
}
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();
}
});
}
// 组件挂载
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
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
← Vue中的设计模式 vue 可复用性和组合 →