Vue 组件从创建到更新,经历了哪些阶段?
Vue 组件的一次更新可以简化为:
当 renderer 遇到组件 VNode 时,会创建组件实例。实例会保存:
Vue 3 中,组件的逻辑主要由 setup() 和 render function 组成;使用 <script setup> 时,编译器会把代码转换为 setup 相关实现。
setup 与 render模板会被编译为 render function。首次渲染时,render effect 执行,读取 count,响应式系统因此建立依赖关系。
setup() 本身通常只在组件创建时执行一次;render 则可能执行多次。不要把每次渲染都要做的逻辑误放进 setup,也不要在 render 中执行副作用。
首次渲染时,Vue 会:
简化流程:
当 render 中读取的响应式数据发生变化时,响应式系统会通知组件的 render effect。组件更新任务进入 scheduler 队列,刷新时重新执行 render,得到新的 VNode。
组件重新执行 render 不代表所有 DOM 都重新创建。renderer 会比较新旧 VNode 的类型、props、子节点和 key,决定复用、更新、移动或删除。
父组件 render 产生新的子组件 VNode 后,Vue 会通过 props、slots 和 VNode 身份协调子组件:
组件更新和 DOM 更新不是同一个概念。组件 render 可能重新执行,但 patch 发现没有变化后,可以不修改 DOM。
常见顺序可以简化为:
Composition API 中对应的生命周期注册方式包括 onMounted、onUpdated、onBeforeUnmount 等。需要读取挂载后的 DOM 时使用 onMounted;需要读取更新后的 DOM 时,通常使用 onUpdated 或等待 nextTick。
生命周期回调也应避免制造无限更新:在 updated 中再次无条件修改响应式状态,可能导致循环更新。
这三者不是同一个东西:
一个组件实例可以在多次更新中生成多个 VNode;一个组件也可能渲染多个 DOM 节点。VNode 是协调过程的中间描述,不是 DOM 本身。
nextTick 在流程中的位置nextTick 等待 Vue 刷新当前更新队列,适合在状态修改后读取 DOM。它不表示网络请求完成,也不保证等待所有未来任务。
setup() 不是每次组件更新都会执行,render 才会反复执行。mounted 只表示首次挂载完成,不会在每次更新后执行。Vue 遇到组件 VNode 时会创建组件实例并执行 setup,随后建立 render effect。首次执行 render 生成 VNode 并完成 mount;响应式依赖变化后,更新任务进入 scheduler,组件重新执行 render,生成新 VNode,再通过 patch 对比新旧 VNode,只更新必要的 DOM。组件实例、VNode 和真实 DOM 是三个不同层次。