Vue实例创建、挂载、更新、卸载全过程
初始化阶段
-
第一步:调用
_init
方法 -
第二步:初始化父子组件关系、事件监听、插槽与渲染函数
-
第三步:创建前,
beforeCreate
触发 -
第四步:注入父组件提供的数据(provide/inject)
-
第五步:初始化数据(核心)
- 初始化数据顺序:
props
---initProps()
methods
---initMethods()
data
---initData()
- 初始化
computed
- 初始化
watch
- 初始化数据顺序:
-
第六步:提供数据给子组件
-
第七步:创建完成,
created
触发 -
最后:自动挂载,调用
vm.$mount()
↓jsVue.prototype._init = function (options) { const vm = this // 合并全局配置与组件配置 vm.$options = mergeOptions(resolveConstructorOptions(vm.constructor), options, vm) // 初始化核心功能 initLifecycle(vm) // 初始化父子组件关系 initEvents(vm) // 事件监听初始化 initRender(vm) // 插槽与渲染函数 callHook(vm, 'beforeCreate') // 触发beforeCreate钩子 initInjections(vm) // 注入父组件提供的数据(早于data/props) initState(vm) // 核心:初始化props/data/methods/watch/computed initProvide(vm) // 提供数据给子组件(晚于data/props) callHook(vm, 'created') // 触发created钩子 // 自动挂载 if (vm.$options.el) vm.$mount(vm.$options.el) }
挂载阶段
-
第一步:调用
vm.$mount()
-
第二步:模板编译
- 将模板转换为渲染函数
- 若使用 template 选项(非单文件组件),Vue 会将模板编译为渲染函数:
template
字符串 → 解析为AST → 优化静态节点 → 生成render函数。
-
最后:调用原始
mount
方法即mountComponent
↓jsVue.prototype.$mount = function (el) { el = el && query(el) const options = this.$options // 解析模板/el为render函数 if (!options.render) { let template = options.template || getOuterHTML(el) const { render } = compileToFunctions(template, {...}) options.render = render } // 调用原始mount方法 return mountComponent(this, el) }
渲染阶段
-
第一步:调用
mountComponent
方法 -
第二步:挂载前,
beforeMount
触发 -
第三步:定义更新函数
updateComponent
render
生成虚拟DOM-VNode树_update
调用patch
生成真实DOM(初次渲染直接创建,更新时进行Diff)- Diff算法(Vue 2):双端对比策略,按同层级比较,优先复用相同key的节点。
- Vue 3优化:Patch Flag标记动态属性,减少比对层级。
-
第四步:创建渲染Watcher
- 触发首次渲染
- 监听依赖变化,触发
updateComponent
重新渲染
-
最后:挂载完成,
mounted
触发jsexport function mountComponent(vm, el) { callHook(vm, 'beforeMount') // 挂载前钩子 // 定义更新函数:render生成VNode → _update转换为真实DOM const updateComponent = () => { vm._update(vm._render(), hydrating) } // 创建渲染Watcher,触发首次渲染 new Watcher(vm, updateComponent, noop, { before() { callHook(vm, 'beforeUpdate') } }, true) // 挂载完成 if (vm.$vnode == null) { vm._isMounted = true callHook(vm, 'mounted') // 触发mounted钩子 } return vm }
jsVue.prototype._render = function () { const { render } = this.$options return render.call(this._renderProxy, this.$createElement) }
jsVue.prototype._update = function (vnode) { const prevVnode = vm._vnode vm.$el = vm.__patch__(prevVnode, vnode) }
渲染阶段详情可看:Vue3渲染机制解析:编译时优化与虚拟DOM的性能跃迁-CSDN博客
更新阶段------数据变更
-
第一步:依赖的Watcher被通知(
mountComponent
中定义的渲染 Watcher)- Vue 2:通过
Dep
和Watcher
实现依赖收集与通知。 - Vue 3:使用
effect
和ReactiveEffect
,基于Proxy的细粒度追踪。
- Vue 2:通过
-
第二步:触发
beforeUpdate
- 注意:应避免再次修改数据,可能会导致循环更新
-
第三步:执行
updateComponent
函数,DOM更新 -
最后:更新完成,触发
updated
js// mountComponent 中定义的渲染 Watcher new Watcher(vm, updateComponent, noop, { before() { // 触发 beforeUpdate if (vm._isMounted && !vm._isDestroyed) { callHook(vm, 'beforeUpdate') } } }, true)
jsfunction callUpdatedHooks(queue) { for (let i = 0; i < queue.length; i++) { const watcher = queue[i] const vm = watcher.vm if (vm._watcher === watcher && vm._isMounted && !vm._isDestroyed) { callHook(vm, 'updated') // 所有 DOM 更新完成后触发 } } }
卸载阶段
-
第一步:调用
vm.$destroy()
-
第二步:卸载前,触发
beforeDestroy
/beforeUnmont
-
第三步:递归销毁子组件
-
第四步:移除数据监听、事件、Watchers
- Vue 3优化 :自动断开
effect
依赖,减少内存泄漏风险。
- Vue 3优化 :自动断开
-
第五步:删除DOM引用
- 删除 DOM 元素对实例的引用
- 删除虚拟节点对父级的引用
-
第六步:卸载完成,触发
destroyed
/unmount
-
最后:移除所有事件监听
jsVue.prototype.$destroy = function () { const vm = this if (vm._isBeingDestroyed) return callHook(vm, 'beforeDestroy') // 触发 beforeDestroy vm._isBeingDestroyed = true // 递归销毁子组件 const parent = vm.$parent if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) { remove(parent.$children, vm) } // 移除数据监听、事件、Watchers if (vm._watcher) vm._watcher.teardown() let i = vm._watchers.length while (i--) vm._watchers[i].teardown() // 删除 DOM 引用 if (vm.$el) vm.$el.__vue__ = null if (vm.$vnode) vm.$vnode.parent = null vm._isDestroyed = true callHook(vm, 'destroyed') // 触发 destroyed vm.$off() // 移除所有事件监听 }
扩展:生命周期钩子与Composition API
- Vue 3的
setup()
- 在
beforeCreate
前执行,替代部分选项式API。
- 在
- 生命周期映射:
beforeCreate
→setup()
created
→setup()
onBeforeMount
、onMounted
等函数式钩子。
<KeepAlive>
- 新增加载状态钩子
onActivated()
和onDeactivated()
- 新增加载状态钩子

实践问题:数据请求应该放在放在哪里?
生命周期/方法 | 执行时机 | 访问 DOM | 适用场景 | 用户体验影响 |
---|---|---|---|---|
created |
组件实例化后,DOM 未生成 | ❌ | 尽早获取数据、SSR | 减少白屏时间 |
mounted |
DOM 已渲染完成 | ✅ | 依赖 DOM 的操作(如地图、图表) | 可能延迟内容展示,页面闪动 |
路由守卫 | 路由切换前 | ❌ | 路由级数据预取 | 无缝过渡 |
setup() + onMounted |
Composition API 模式 | ✅ | Vue 3 项目,代码组织更灵活 | 类似 mounted |
Nuxt.js asyncData |
服务端或客户端初始化前 | ❌ | SSR 数据预取 | 首屏直出,无闪烁 |
总结
- 默认选择
created
(或setup
内直接调用) - 需要操作 DOM 时使用
mounted
- SSR/SEO 优化使用路由守卫或 Nuxt.js 方案
- 在请求前后设置
loading
状态,配合骨架屏提升体验。
参考资料 www.cnblogs.com/gerry2019/p... juejin.cn/post/684490... vue3js.cn/interview/v...