上一篇分析了VUE响应式系统的建立过程,最后遗留了一个小问题,更改message变量后,页面是否更新呢?下面就看看这一过程。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
</head>
<body>
<div id="app">{{message}}</child></div>
<script>
var app = new Vue({
el:'#app',
data:{
message:'Hello World'
}
})
settimeout(functions(){
app.message = "hellow Vue";
}, 5000)
</script>
</body>
</html>
我们在最后面加了一个5秒定时器,改变message变量的值。看看改变这个message值后的过程。
改变message值肯定调用了set函数,看看这个set函数:
set: function reactiveSetter (newVal) {
var value = getter ? getter.call(obj) : val;
/* eslint-disable no-self-compare */
if (newVal === value || (newVal !== newVal && value !== value)) {
return
}
/* eslint-enable no-self-compare */
if (customSetter) {
customSetter();
}
// #7981: for accessor properties without setter
if (getter && !setter) { return }
if (setter) {
setter.call(obj, newVal);
} else {
val = newVal;
}
childOb = !shallow && observe(newVal);
dep.notify();
}
在set 函数中设置了新值:“Hello Vue" ,并且调用了dep.notify函数,看看这个函数:
Dep.prototype.notify = function notify () {
// stabilize the subscriber list first
var subs = this.subs.slice();
if (!config.async) {
// subs aren't sorted in scheduler if not running async
// we need to sort them now to make sure they fire in correct
// order
subs.sort(function (a, b) { return a.id - b.id; });
}
for (var i = 0, l = subs.length; i < l; i++) {
subs[i].update();
}
};
遍历dep对象中加入的Watcher ,执行Watcher的update 方法。我们这个例子中只计入了render Watcher。当然可以加入其它Watcher,例如computed Watcher,我们下一篇文章分析计算属性时就看到了。接下来看看Watcher的update 方法:
Watcher.prototype.update = function update () {
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true;
} else if (this.sync) {
this.run();
} else {
queueWatcher(this);
}
};
render Watcher 不lazy, 也不sync 所以进入queueWatcher, 一步步往后走,最后进入了Watcher 的 run方法:
Watcher.prototype.run = function run () {
if (this.active) {
var value = this.get();
if (
value !== this.value ||
// Deep watchers and watchers on Object/Arrays should fire even
// when the value is the same, because the value may
// have mutated.
isObject(value) ||
this.deep
) {
// set new value
var oldValue = this.value;
this.value = value;
if (this.user) {
try {
this.cb.call(this.vm, value, oldValue);
} catch (e) {
handleError(e, this.vm, ("callback for watcher \"" + (this.expression) + "\""));
}
} else {
this.cb.call(this.vm, value, oldValue);
}
}
}
};
我们看到了run方法里面执行了Watcher的get方法,再一次看看这个get方法:
Watcher.prototype.get = function get () {
pushTarget(this);
var value;
var vm = this.vm;
try {
value = this.getter.call(vm, vm);
} catch (e) {
if (this.user) {
handleError(e, vm, ("getter for watcher \"" + (this.expression) + "\""));
} else {
throw e
}
} finally {
// "touch" every property so they are all tracked as
// dependencies for deep watching
if (this.deep) {
traverse(value);
}
popTarget();
this.cleanupDeps();
}
return value
};
this.getter.call 其实执行到了render Watcher的回调函数updateComponent,这个回调函数就是用来创建VNode,创建真实Dom的。而这时message已让我们改变成了Hello Vue 所以,当我们改变message 变量时,页面重新进行了渲染。
至此整个Vue响应式系统怎么运行的也理清了。