Vue 实例生命周期:从创建到卸载的全过程

Vue实例创建、挂载、更新、卸载全过程

  1. 初始化阶段
    • 第一步:调用_init方法

    • 第二步:初始化父子组件关系、事件监听、插槽与渲染函数

    • 第三步:创建前,beforeCreate触发

    • 第四步:注入父组件提供的数据(provide/inject)

    • 第五步:初始化数据(核心)

      • 初始化数据顺序:
        • propsinitProps()
        • methodsinitMethods()
        • datainitData()
        • 初始化computed
        • 初始化watch
    • 第六步:提供数据给子组件

    • 第七步:创建完成,created触发

    • 最后:自动挂载,调用vm.$mount()

      Vue.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)
      }
      
  2. 挂载阶段
    • 第一步:调用 vm.$mount()

    • 第二步:模板编译

      • 将模板转换为渲染函数
      • 若使用 template 选项(非单文件组件),Vue 会将模板编译为渲染函数:
        • template字符串 → 解析为AST → 优化静态节点 → 生成render函数。
    • 最后:调用原始mount方法即mountComponent

      Vue.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)
      }
      
  3. 渲染阶段
    • 第一步:调用mountComponent方法

    • 第二步:挂载前,beforeMount触发

    • 第三步:定义更新函数updateComponent

      • render生成虚拟DOM-VNode树
      • _update调用patch生成真实DOM(初次渲染直接创建,更新时进行Diff)
        • Diff算法(Vue 2):双端对比策略,按同层级比较,优先复用相同key的节点。
        • Vue 3优化:Patch Flag标记动态属性,减少比对层级。
    • 第四步:创建渲染Watcher

      • 触发首次渲染
      • 监听依赖变化,触发updateComponent重新渲染
    • 最后:挂载完成,mounted触发

      export 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
      }
      
      Vue.prototype._render = function () {
        const { render } = this.$options
        return render.call(this._renderProxy, this.$createElement)
      }
      
      Vue.prototype._update = function (vnode) {
        const prevVnode = vm._vnode
        vm.$el = vm.__patch__(prevVnode, vnode) 
      }
      

      渲染阶段详情可看:Vue3渲染机制解析:编译时优化与虚拟DOM的性能跃迁-CSDN博客

  4. 更新阶段——数据变更
    • 第一步:依赖的Watcher被通知(mountComponent 中定义的渲染 Watcher)

      • Vue 2:通过DepWatcher实现依赖收集与通知。
      • Vue 3:使用effectReactiveEffect,基于Proxy的细粒度追踪。
    • 第二步:触发beforeUpdate

      • 注意:应避免再次修改数据,可能会导致循环更新
    • 第三步:执行updateComponent函数,DOM更新

    • 最后:更新完成,触发updated

      // mountComponent 中定义的渲染 Watcher
      new Watcher(vm, updateComponent, noop, {
        before() { // 触发 beforeUpdate
          if (vm._isMounted && !vm._isDestroyed) {
            callHook(vm, 'beforeUpdate')
          }
        }
      }, true)
      
      function 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 更新完成后触发
          }
        }
      }
      
  5. 卸载阶段
    • 第一步:调用vm.$destroy()

    • 第二步:卸载前,触发beforeDestroy/beforeUnmont

    • 第三步:递归销毁子组件

    • 第四步:移除数据监听、事件、Watchers

      • Vue 3优化:自动断开effect依赖,减少内存泄漏风险。
    • 第五步:删除DOM引用

      • 删除 DOM 元素对实例的引用
      • 删除虚拟节点对父级的引用
    • 第六步:卸载完成,触发destroyed/unmount

    • 最后:移除所有事件监听

      Vue.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() // 移除所有事件监听
      }
      
  6. 扩展:生命周期钩子与Composition API
    • Vue 3的setup()
      • beforeCreate前执行,替代部分选项式API。
    • 生命周期映射:
      • beforeCreatesetup()
      • createdsetup()
      • onBeforeMountonMounted等函数式钩子。
      • 新增加载状态钩子onActivated()onDeactivated()

Vue 实例生命周期:从创建到卸载的全过程_第1张图片

实践问题:数据请求应该放在放在哪里?
生命周期/方法 执行时机 访问 DOM 适用场景 用户体验影响
created 组件实例化后,DOM 未生成 尽早获取数据、SSR 减少白屏时间
mounted DOM 已渲染完成 依赖 DOM 的操作(如地图、图表) 可能延迟内容展示,页面闪动
路由守卫 路由切换前 路由级数据预取 无缝过渡
setup() + onMounted Composition API 模式 Vue 3 项目,代码组织更灵活 类似 mounted
Nuxt.js asyncData 服务端或客户端初始化前 SSR 数据预取 首屏直出,无闪烁

总结

  1. 默认选择 created(或 setup 内直接调用)
  2. 需要操作 DOM 时使用 mounted
  3. SSR/SEO 优化使用路由守卫或 Nuxt.js 方案
  4. 在请求前后设置 loading 状态,配合骨架屏提升体验。

参考资料
https://www.cnblogs.com/gerry2019/p/12001661.html
https://juejin.cn/post/6844903811094413320
https://vue3js.cn/interview/vue/lifecycle.html#%E4%B8%80%E3%80%81%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E6%98%AF%E4%BB%80%E4%B9%88

你可能感兴趣的:(vue3,vue.js,javascript,前端,vue,ajax)