Title:vue2 生命周期详解

在探索vue生命周期之前,咱们先提出一个问题:

new Vue()之后,产生了什么?,当数据(data)发生变化之后又产生了什么?

生命周期流程图

由上图可知,new Vue()之后,别离通过了以上几个阶段,别离是初始化阶段模板编译阶段挂载阶段更新阶段销毁阶段,那每一个阶段都干了些什么事呢?

初始化阶段

  1. 首先做一些初始化操作,次要是设置一些公有属性到vue实例中。
  2. 运行生命周期钩子函数beforeCreate
  3. 进入注入流程,解决属性,computedmethodsdataprovideinject,最初应用代理模式将这些属性挂载到实例中。
  4. 运行生命周期钩子函数created

编译阶段

  1. 生成render函数:如果有配置,间接应用配置的render函数,如果没有,应用运行时编译器,把模板编译成render函数。

挂载阶段

  1. 运行生命周期钩子函数beforeMount
  2. 创立一个Watcher,传入一个函数updeteCompontent,该函数会运行render,函数,并把render函数的返回后果vnode作为参数给_updete函数执行。

    // 伪代码updateCompontent(){  _update(_render())}new Watcher(updateCompontent)

在执行render函数的过程中会收集所有依赖,未来依赖产生变换时会呈现执行updateCompontent函数。

在执行_update的过程中,会触发patch函数,因为目前还没有就的虚构DOM树,因而间接为以后的虚构DOM树的每一个节点生成对应elm属性,即实在DOM。

如果遇到创立一个组件实例的vnode,则会进入组件实例化流程,该流程同vue实例流程,同上初始化阶段,编译阶段,挂载阶段。最终会把创立好的组件实例挂载到vnodecompontentInstance属性中,以便复用。

  1. 运行生命周期钩子函数mounted

更新阶段

  1. 数据发生变化后,所有依赖该数据的watcher都会从新运行。
  2. watcher会被调度器Scheduler放到nextTick中运行,参考vue数据响应式原理,也就是微队列中,这样防止防止多个依赖的数据同时扭转后被屡次执行。
  3. 运行生命周期钩子函数beforeUpdate
  4. updateCompontent函数从新执行:

    // 伪代码updateCompontent(){  _update(_render())}new Watcher(updateCompontent)

    在执行render函数的过程中,会先去掉之前的依赖,从新收集新的依赖,未来依赖发生变化时呈现运行updateCompontent函数。

    在执行update函数的过程中,会触发patch函数,比照新旧两棵DOM树:

    当比照两棵DOM树的节点的时候,有两种状况,别离:

    • 一般html节点

      一般html节点的比照会导致实在节点被创立,删除,挪动,更新

    • 组件节点

      组件节点的比照会导致组件被创立,删除,挪动,更新。

      a)组件节点创立的时,进入组件实例化流程,同上初始化阶段,编译阶段,挂载阶段

      b)当旧组件节点删除时,会调用旧组件的$destroy办法删除组件,该办法会触发生命周期钩子函数beforeDestroy,而后递归调用组件的$destroy办法,而后登程生命周期钩子函数destroyed

      c)当组件更新时,相当于组件的updateCompontent函数被从新触发,进入渲染流程,同更新阶段

  1. 运行生命周期钩子函数updated

销毁阶段

  1. 当组件销毁的时候,会调用组件的$destroy办法删除组件,该办法会调用beforeDestroydestroyed办法
Vue.prototype.$destroy = function () {  // 该属性标记着以后实例是否处于正在被销毁的状态  if (vm._isBeingDestroyed) {    return  }  callHook(vm, 'beforeDestroy')  vm._isBeingDestroyed = true  // remove self from parent  const parent = vm.$parent  if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {    remove(parent.$children, vm)  }  // ...  callHook(vm, 'destroyed')  // ...  vm.$off()}
  1. 执行beforeDestroy办法后,将以后组件实例从父组件实例的$children中删除

    if (parent && !parent._isBeingDestroyed && !vm.$options.abstract) {    remove(parent.$children, vm)  }
  1. 移除本身的依赖监听和事件监听,实例内响应式数据的援用

    // teardown watchersif (vm._watcher) {  vm._watcher.teardown()}let i = vm._watchers.lengthwhile (i--) {  vm._watchers[i].teardown()}if (vm._data.__ob__) {  vm._data.__ob__.vmCount--}vm._isDestroyed = truevm.__patch__(vm._vnode, null)
  1. 执行destroyed办法后,通过vm.$off()办法移除实例上的所有事件监听器,

扩大

回到结尾那个问题,new Vue()之后,产生了什么?,当数据(data)发生变化之后又产生了什么?销毁呢?

// main.jsimport Vue from "vue";import App from "./App.vue";Vue.config.productionTip = false;new Vue({  render: (h) => h(App),  beforeCreate() {    console.log("vue实例 beforeCreate");  },  created() {    console.log("vue实例 created");  },  beforeMount() {    console.log("vue实例 beforeMount");  },  mounted() {    console.log("vue实例 mounted", this);  },  beforeUpdate() {    console.log("vue实例 beforeUpdate");  },  updated() {    console.log("vue实例 updated");  },  beforeDestroy() {    console.log("vue实例 beforeDestroy");  },  destroyed() {    console.log("vue实例 destroyed");  },}).$mount("#app");
// App<template>  <div id="app">    <h1>App</h1>    <A v-if="show" :count="count" />    <button @click="count++">increase</button>    <button @click="show = !show">toggle</button>  </div></template><script>import A from "./A.vue";export default {  components: { A },  data() {    return {      show: true,      count: 0,    };  },  beforeCreate() {    console.log("App beforeCreate");  },  created() {    console.log("App created");  },  beforeMount() {    console.log("App beforeMount");  },  mounted() {    console.log("App mounted");  },  beforeUpdate() {    console.log("App beforeUpdate");  },  updated() {    console.log("App updated");  },  beforeDestroy() {    console.log("App beforeDestroy");  },  destroyed() {    console.log("App destroyed");  },};</script>
// A<template>  <div>    <h1>A compnent: {{ count }}</h1>    <B :count="count" />  </div></template><script>import B from "./B.vue";export default {  components: { B },  props: ["count"],  beforeCreate() {    console.log("A beforeCreate");  },  created() {    console.log("A created");  },  beforeMount() {    console.log("A beforeMount");  },  mounted() {    console.log("A mounted");  },  beforeUpdate() {    console.log("A beforeUpdate");  },  updated() {    console.log("A updated");  },  beforeDestroy() {    console.log("A beforeDestroy");  },  destroyed() {    console.log("A destroyed");  },};</script><style></style>
// B<template>  <div>    <h1>B compnent: {{ count }}</h1>  </div></template><script>export default {  props: ["count"],  beforeCreate() {    console.log("B beforeCreate");  },  created() {    console.log("B created");  },  beforeMount() {    console.log("B beforeMount");  },  mounted() {    console.log("B mounted");  },  beforeUpdate() {    console.log("B beforeUpdate");  },  updated() {    console.log("B updated");  },  beforeDestroy() {    console.log("B beforeDestroy");  },  destroyed() {    console.log("B destroyed");  },};</script><style></style>

new Vue()后,会递归创立vue实例和组件实例:初始化阶段模板编译阶段挂载阶段

当所有子组件实例创立实现后vue实例才创立实现

vue实例 beforeCreate
vue实例 created
vue实例 beforeMount

App beforeCreateApp createdApp beforeMount    A beforeCreate    A created    A beforeMount        B beforeCreate        B created        B beforeMount        B mounted    A mountedApp mounted

vue实例 mounted

当数据变动时

App beforeUpdate

A beforeUpdate    B beforeUpdate    B updatedA updated

App updated

当组件A,B销毁时

App beforeUpdate

A beforeDestroy    B beforeDestroy    B destroyedA destroyed

App updated

以上仅集体了解,如有不当之处还请不吝赐教