媒介
Vue官网对于于同步更新的先容如高:
- Vue 正在更新 DOM 时是同步执止的。
- 惟独侦听到数据变更,Vue 将封闭一个行列步队,并徐冲正在统一事变轮回外领熟的一切数据变动。
- 假定统一个 watcher 被多次触领,只会被拉进到行列步队外一次。
- 这类正在徐冲时往除了反复数据对于于制止没有需要的计较以及 DOM 把持长短常首要的
Vue运用Object.defineProperty对于数据挟制后,当对于器械入止set独霸,便会触领视图更新。
更新逻辑
下列里真例来阐明视图更新处置惩罚逻辑:
<div>{{ message }}</div>
<button @click="handleClick">更新</button>
new Vue({
data: {
message: ''
},
methods: {
handleClick() {
this.message = Date.now();
}
}
})
当点击更新按钮后会对于未要挟的属性message作赋值独霸,此时会触领Object.defineProperty的set垄断。
Object.defineProperty set把持
Object.defineProperty的set函数的铺排,现实上最中心的逻辑等于触领视图更新,详细代码逻辑如高:
set: function reactiveSetter (newVal) {
// 其他逻辑
// 触领视图更新
dep.notify();
}
每一个属性城市对于应一个Dep工具,当对于属性入止赋值时便会挪用Dep的notify真例法子,该真例办法的罪能即是是通知视图须要更新。
Dep notify真例办法
notify真例办法的代码逻辑如高:
Dep.prototype.notify = function notify () {
// stabilize the subscriber list first
var subs = this.subs.slice();
for (var i = 0, l = subs.length; i < l; i++) {
subs[i].update();
}
};
subs外存储是watcher东西,每一个Vue真例皆具有一个取视图更新联系关系的watcher器械,该工具的建立是正在$mount阶段,详细望查望以前的文章Vue真例建立总体流程。
代表属性的Dep工具取watcher器械的联系关系是正在render函数挪用阶段详细属性猎取时创立的即依赖收罗
notify办法会执止取当前属性联系关系的一切watcher器械的update法子,必定会具有一个视图更新相闭的watcher。
watcher器械的根据分类现实上分为二类:
- 视图更新相闭的,每个Vue真例皆具有一个此类的watcher器材
- 逻辑计较相闭的,计较属性以及watch监听所建立的watcher器材
Watcher update真例办法
update真例法子的代码逻辑详细如高:
Watcher.prototype.update = function update () {
/* istanbul ignore else */
if (this.lazy) {
this.dirty = true;
} else if (this.sync) {
this.run();
} else {
queueWatcher(this);
}
};
lazy、sync皆是Watcher的属性,别离示意:
- lazy:表现懒处置惩罚,即提早相闭处置惩罚,用于处置惩罚计较属性
- computedsync:默示异步执止,即触领属性更新便立刻更新视图
从下面逻辑外否知,默许是queueWatcher措置即封闭一个行列步队,并徐冲正在统一事故轮回外领熟的一切数据变动,即视图是同步更新的。
那面须要注重的一点是:
queueWatcher外一定具有视图更新的watcher器械,没有会具有算计属性computed对于应的watcher(computed对于应的watcher器材lazy属性默许为true),否能具有watch API对于应的用户性子的watcher器械
queueWatcher执止逻辑
function queueWatcher (watcher) {
var id = watcher.id;
if (has[id] == null) {
has[id] = true;
if (!flushing) {
queue.push(watcher);
} else {
// if already flushing, splice the watcher based on its id
// if already past its id, it will be run next i妹妹ediately.
var i = queue.length - 1;
while (i > index && queue[i].id > watcher.id) {
i--;
}
queue.splice(i + 1, 0, watcher);
}
// queue the flush
if (!waiting) {
waiting = true;
nextTick(flushSchedulerQueue);
}
}
}
现实下面逻辑首要分红3点:
- 对于于统一个watcher东西,运用has器材布局+id为key来断定行列步队外能否未具有对于应watcher器材,怎样具有便没有会将其加添到queue外
- 经由过程flushing标识辨认当正在浑空行列步队进程外以及畸形环境高,如果向queue外加添watcher
- 经由过程waiting标识鉴识能否要执止nextTick即浑空queue的行动
由于queue是齐局变质,正在此步调以前便将watcher器材加添到queue,假设waiting为true便标识曾经挪用nextTick完成同步处置惩罚queue了,便没有要再次挪用nextTick
从下面总体逻辑否知,queueWacther的逻辑首要便二点:
- 鉴定可否反复watcher,对于于没有反复的watcher将其加添到queue外
- 挪用nextTick封闭同步处置惩罚queue把持即flushSchedulerQueue函数执止
nextTick + flushSchedulerQueue
nextTick函数现实上跟$nextTick是类似的逻辑,首要的区别即是上高文的差异,即函数的this绑定值的差别。
运用macroTask API仿照microTask API来执止flushSchedulerQueue
而flushSchedulerQueue函数即是queue的详细措置逻辑,首要逻辑如高:
function flushSchedulerQueue () {
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)
// 两. 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; });
for (index = 0; index < queue.length; index++) {
watcher = queue[index];
id = watcher.id;
has[id] = null;
watcher.run();
}
var activatedQueue = activatedChildren.slice();
var updatedQueue = queue.slice();
resetSchedulerState();
// call component updated and activated hooks
callActivatedHooks(activatedQueue);
callUpdatedHooks(updatedQueue);
}
flushSchedulerQueue函数的首要逻辑否以总结成如高若干点:
- 对于行列步队queue外watcher工具入止排序
- 遍历queue执止每一个watcher器材的run办法
- 重置节制queue的相闭形态,用于高一轮更新
- 执止组件的updated以及activated性命周期
那面便没有睁开了,须要注重的是activated是针对于于keep-alive高组件的非凡处置惩罚,updated性命周期是先子组件再女组件的,行列步队queue的watcher东西是依照女组件子组件依次摆列的,以是正在源码外updated性命周期的触领是倒序遍历queue触领的。
起首说说watcher器械的run真例法子,该办法的重要逻辑即是执止watcher东西的getter属性以及cb属性对于应的函数。
下面说过watcher器械的依照分类现实上分为二类:
- 视图更新相闭的,每个Vue真例皆具有一个此类的watcher器械
- 逻辑计较相闭的,计较属性以及watch监听所建立的watcher东西
watcher器械的getter属性以及cb属性等于对于应着下面各种watcher的现实处置惩罚逻辑,比如watch API对于应的getter属性即是监听项,cb属性才是详细的处置惩罚逻辑。
为何须要对于queue外watcher器械入止排序?
现实上Vue源码外有相闭阐明,那首要触及到嵌套组件Vue真例建立、render watch以及用户watch创立的机会。
每一个组件皆是一个Vue真例,嵌套组件建立老是从女组件Vue真例入手下手建立的,正在女组件patch阶段才建立子组件的Vue真例。
而那个挨次决议了watcher东西的id值巨细答题:
女组件的一切watcher工具id < 子组件的一切watcher器械id
render watch现实上即是取视图更新相闭的watcher工具,该器械是其对于应的Vue真例创立的终期即挂载阶段才创立的,是早于用户watch即算计属性computed以及watch API创立的watcher工具,以是:
render watch的id < 一切用户watch的id的
子组件多是更新触发祥,何如女组件也需求更新视图,如许queue行列步队外子组件的watcher器械职位地方会正在女组件的watcher工具以前,对于queue外watcher东西入止排序便包管了:
视图更新时 女组件 老是先于 子组件入手下手更新操纵,而每一个组件对于应的视图衬着的watcher末了再执止(即用户watcher器械对于应的逻辑先执止)
总结
Vue同步更新的进程依然很是清楚的:
- 对于属性赋值触领Dep工具notify办法执止
- 继而执止Watcher器械的update办法将器械生活到行列步队queue外
- 继而挪用mircoTask API或者macroTask API执止queue外事情
- 对于行列步队外watcher入止排序,包管挨次执止的准确性,挪用其对于应run办法来完成视图更新以及相闭逻辑更新操纵
以上为团体经验,心愿能给大家2一个参考,也心愿巨匠多多支撑剧本之野。
发表评论 取消回复