关于前端:vue2-生命周期详解

6次阅读

共计 4831 个字符,预计需要花费 13 分钟才能阅读完成。

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 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)
  1. 执行 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

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

正文完
 0