vue2 生命周期详解

Title:vue2 生命周期详解

Date:2022-05-03

在探究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属性中,以便复用。

  3. 运行生命周期钩子函数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函数被重新触发,进入渲染流程,同更新阶段

  5. 运行生命周期钩子函数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)
      }
    
  2. 移除自身的依赖监听和事件监听,实例内响应式数据的引用

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

扩展

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

在这里插入图片描述

// main.js

import 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 beforeCreate
App created
App beforeMount
A beforeCreate
A created
A beforeMount
B beforeCreate
B created
B beforeMount
B mounted
A mounted
App mounted
vue实例 mounted

在这里插入图片描述

当数据变化时

App beforeUpdate
A beforeUpdate
B beforeUpdate
B updated
A updated
App updated

在这里插入图片描述

当组件A,B销毁时

App beforeUpdate
A beforeDestroy
B beforeDestroy
B destroyed
A destroyed
App updated

在这里插入图片描述

以上仅个人理解,如有不当之处还请不吝赐教

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值