简言
vue3更新流程分析,下面的无论是代码分析还是流程图都不会太往深走,会乱;其中涉及到的其他重要东西,比如响应式,编译器…后面重新写文章去分析
分析的示例代码
<div id="#app">
<h1>vue3 源码更新流程</h1>
<div>{{ counter }}</div>
</div>
<script>
Vue.createApp({
data() {
return {
counter: 0
}
},
mounted() {
setInterval(() => {
this.counter++
}, 1000)
}
})
</script>
流程图
源码分析
这里咱们按照两个方面去分析
下面所有的代码都是经过简化处理过的,和更新流程不相干的我都会剔除掉,避免理解混乱
1. 更新机制的建立
所在的函数:setupRenderEffect
queueJob:就是将组件的更新函数(也就是effect.update --> run --> componentUpdateFn)放入到更新队列中
const setUpRenderEffect = () => {
// 组件真正的更新函数
// 告诉组件如果数据发生变化,我该怎么做才能更新组件
// 在首次执行渲染的时候,其实已经建立了依赖关系(在之前文章中vue初始化有提到)
const componentUpdateFn = () => {}
const effect = (instance.effect = new ReactiveEffect(
componentUpdateFn,
() => queueJob(update),
instance.scope // 不需要关心,和更新流程无关
))
// 实际上update函数就是effect.run
const update = (instance.update = () => effect.run())
}
function queueJob {
// 这边的代码逻辑要清除,其实只要懂一点,它在温壶queue这个更新队列
if (job.id == null) {
queue.push(job)
} else {
queue.splice(findInsertionIndex(job.id), 0, job))
}
// 重要代码: 启动批量任务执行
queueFlush()
}
function queueFlush() {
// 只有处在空闲中的时候才会开始更新
if (!isFlushing || !isFlushPending) {
// 代表正在工作中
isFlushPending = true
// 重要代码
// 已异步方式(微任务)去执行flushJobs,在未来的某一时刻会开始刷新queue这个队列
currentFlushPromise = resolvePromise.then(flushJobs)
}
}
function flushJobs() {
循环执行queue里面的每一项
// queue里的每一项就是effect.update
effect.update ⇒ effect.run ⇒ fn ⇒ componentUpdateFn ⇒ patch (diff算法开始执行)
}
2. 数据改变到放入更新队列
const PublicInstanceProxyHandlers = () => {
get() {} // 很重要,但是在更新流程中不用关心
set() {
// 注意此时data是一个new Proxy构建的数据
data[key] = value
}
}
// baseHandlers.js
function createSetter() {
// 最最重要的就是
trigger()
}
function trigger() {
// 在数据访问的时候,用户执行track -> trackEffect,会维护一个对象的key和组件更新函数之间关系的map,
// 结构大约是这样的
const deps = Map(
counter: Set([ReactiveEffect])
)
// 这个函数的作用就是取出来更新函数,然后执行triggerEffect()
triggetEffect()
}
function triggetEffect() {
if (effect.scheduler) {
// 异步更新
// 最终都是走componentUpdateFn
// effect.scheduler 会调用的是 queueJob(update),
// 整个流程就清晰起来了
effect.scheduler()
} else {
// 立刻更新
effect.run()
}
}