Vue生命周期以及父子组件加载顺序

lifecycle

初始化 - beforeCreate & created

beforeCreate:初始化 vue 实例。data methods 等尚未被初始化,无法调用。

created:vue 实例初始化完成,完成响应式绑定。data methods 等都已初始化完成,可调用。尚未开始渲染模板。

注意:beforeCreate & created 并没有渲染 DOM,不能够访问 DOM。如果组件在加载的时候需要和后端有交互,如果是需要访问 propsdata 等数据的话,就需要使用 created 钩子函数。

/**
* 
* vue2.6.X源码地址src/core/instance/init.js
* beforeCreate 和 created 函数都是在实例化 Vue 的阶段,在 _init 方法中执行的。
*
**/

Vue.prototype._init = function (options?: Object) {
    vm._self = vm
    initLifecycle(vm)   // 初始化生命周期相关的属性、相关属性赋值
    initEvents(vm)      // 初始化事件队列以及监听器
    
    // 通过defineProperty的set去notify()通知subscribers有值被修改,并执行watchers的update函数
    // attrs/$listeners的响应化
    initRender(vm)      
    callHook(vm, 'beforeCreate')
    
    initInjections(vm)  // resolve injections before data/props
    initState(vm)       // initState 的作用是初始化 props、data、methods、watch、computed 等属性
    initProvide(vm)     // resolve provide after data/props
    callHook(vm, 'created')
}
beforeCreate() {
    this.$data      // undefined
    this.$el        // undefined
    this.$computed  // undefined
    this.$methods   // undefined
}
    
created() {
    this.$data      // {__ob__: Observer}
    this.$el        // undefined
    this.$computed  // 可以访问到计算属性
    this.$methods   // 可以访问到methods中的方法
}   

挂载 - beforeMount & mounted

beforeMount:编译模板,调用 render 函数生成虚拟 V-Dom 。但还没有开始渲染真实Dom。

mounted:完成DOM渲染 。组件创建完成。开始由创建阶段进入运行阶段。

/**
*
* vue2.6.X源码地址src/core/instance/lifecycle.js
* beforeMount 钩子函数发生在 mount,也就是 DOM 挂载之前,它的调用时机是在 mountComponent 函数中
*
**/
export function mountComponent (
  vm: Component,
  el: ?Element,
  hydrating?: boolean
): Component {
  vm.$el = el
  // ...
  callHook(vm, 'beforeMount')

  let updateComponent
  /* istanbul ignore if */
  if (process.env.NODE_ENV !== 'production' && config.performance && mark) {
    updateComponent = () => {
      const name = vm._name
      const id = vm._uid
      const startTag = `vue-perf-start:${id}`
      const endTag = `vue-perf-end:${id}`

      mark(startTag)
      const vnode = vm._render()
      mark(endTag)
      measure(`vue ${name} render`, startTag, endTag)

      mark(startTag)
      vm._update(vnode, hydrating)
      mark(endTag)
      measure(`vue ${name} patch`, startTag, endTag)
    }
  } else {
    updateComponent = () => {
      vm._update(vm._render(), hydrating)
    }
  }

  // we set this to vm._watcher inside the watcher's constructor
  // since the watcher's initial patch may call $forceUpdate (e.g. inside child
  // component's mounted hook), which relies on vm._watcher being already defined
  new Watcher(vm, updateComponent, noop, {
    before () {
      if (vm._isMounted) {
        callHook(vm, 'beforeUpdate')
      }
    }
  }, true /* isRenderWatcher */)
  hydrating = false

  // manually mounted instance, call mounted on self
  // mounted is called for render-created child components in its inserted hook
  if (vm.$vnode == null) {
    vm._isMounted = true
    callHook(vm, 'mounted')
  }
  return vm
}
// 在挂载开始之前被调用,beforeMount之前,会找到对应的template,并编译成render函数。
beforeMount{
    this.$data      // {__ob__: Observer}
    this.$el        // undefined
    this.$computed  // 可以访问到计算属性
    this.$methods   // 可以访问到methods中的方法
}

// 实例挂载到DOM上,此时可以通过DOM API获取到DOM节点,$ref属性可以访问。    
mounted() {
    this.$data      // {__ob__: Observer}
    this.$el        // <div id="app"></div>
    this.$computed  // 可以访问到计算属性
    this.$methods   // 可以访问到methods中的方法
}  

更新 - beforeUpdate & updated

/**
* 
* vue2.6.X源码地址src/core/observer/scheduler.js
* update 的执行时机是在flushSchedulerQueue 函数调用的时候
*
**/

beforeUpdate

Vue具有响应式原理,即能够监测到数据的变化。当视图层的数据即将被更新前,就会触发这个生命周期,这个阶段主要可以用在得知哪个组件即将发生数据改动,并且可以移除对其绑定的事件监听器。

updated

此阶段已经重新渲染完成数据更新后的状态。

注意,尽量不要在 updated 中继续修改数据,否则可能会触发死循环。如果要更改官方建议使用computedwatch来进行数据更改。

销毁 - beforeDestroy & destroyed - vue2.0版本

beforeDestroy

当组件销毁前会进行的操作,卸载组件实例后调用。实例仍然是完全正常的。移除、解绑一些全局事件、自定义事件,可以在此时操作。

destroyed

组件被销毁。子组件也已销毁。但是父组件已经渲染在 DOM 上的视图仍然会保留在页面上,只有子组件会完全消失。

销毁 - beforeDestroy & destroyed - vue3.0版本

beforeUnmount

当组件销毁前会进行的操作,卸载组件实例后调用。实例仍然是完全正常的。移除、解绑一些全局事件、自定义事件,可以在此时操作。

unmounted

组件被销毁。子组件也已销毁。但是父组件已经渲染在 DOM 上的视图仍然会保留在页面上,只有子组件会完全消失。

特殊钩子函数 - activated、deactivated - keep-alive组件

activated:被 keep-alive 缓存的组件激活时调用。

deactivated:被 keep-alive 缓存的组件停用时调用。

如何正确的操作 DOM

mountedupdated 都不会保证所有子组件都挂载完成。如果想等待所有视图都渲染完成,需要使用 $nextTick

mounted() {
  this.$nextTick(function () {
    // 仅在整个视图都被渲染之后才会运行的代码
  })
}

VUE3.0 Composition API 生命周期有何不同

  • setup 代替了 beforeCreatecreated
  • 生命周期换成了函数的形式,如 mounted -> onMounted文档
import { onUpdated, onMounted } from 'vue'

export default {
    setup() {
        onMounted(() => {
            console.log('mounted')
        })
        onUpdated(() => {
            console.log('updated')
        })
    } 
}

Axios等请求数据请求放在哪个生命周期合适?

一般有两个选择:createdmounted ,建议选择后者 mounted

执行速度

  • 从理论上来说,放在 created 确实会快一些
  • 但 ajax 是网络请求,其时间是主要的影响因素。从 createdmounted 是 JS 执行,速度非常快。
  • 所以,两者在执行速度上不会有肉眼可见的差距

代码的阅读和理解

  • 放在 created 却会带来一些沟通和理解成本,从代码的执行上来看,它会一边执行组件渲染,一边触发网络请求,并行
  • 放在 mounted 就是等待 DOM 渲染完成再执行网络请求,串行,好理解。

所以,综合来看,更建议选择 mounted

Vue父子组件的执行顺序

加载渲染时

父组件 beforeCreate
父组件 beforeCreate
父组件 created
父组件 beforeMount
子组件 beforeCreate
子组件 created
子组件 beforeMount
子组件 mounted
父组件 mounted

更新时

父组件 beforeUpdate
子组件 beforeUpdate
子组件 updated
父组件 updated

销毁时

父组件 beforeDestroy
子组件 beforeDestroy
子组件 destroyed
父组件 destoryed

参考链接

Vue.js 生命周期 https://ustbhuangyi.github.io/vue-analysis/v2/components/lifecycle.html#beforeupdate-updated

Vue源码解析 https://blog.csdn.net/qq_46299172/article/details/107657663?spm=1001.2014.3001.5502

Vue生命周期及父子组件生命周期的加载顺序 https://www.cnblogs.com/yyy0926/p/15654272.html

posted @ 2022-05-09 02:05  Scok  阅读(2069)  评论(0编辑  收藏  举报