首頁 > 軟體

vue2與vue3中生命週期執行順序的區別說明

2022-06-29 18:01:26

vue2與vue3中生命週期執行順序區別

生命週期比較

  • vue2中執行順序 beforeCreate=>created=>beforeMount =>mounted=>beforeUpdate =>updated=>beforeDestroy=>destroyed
  • vue3中執行順序 setup=>onBeforeMount=>onMounted=>onBeforeUpdate=>onUpdated=>onBeforeUnmount=>onUnmounted

對應關係

vue2->vue3

  • beforeCreate->setup
  • created -> setup
  • beforeMount -> onBeforeMount
  • mounted -> onMounted
  • beforeUpdate -> onBeforeUpdate
  • updated -> onUpdated
  • beforeDestroy -> onBeforeUnmount
  • destroyed -> onUnmounted

其中 vue3中的setup相當於vue2中beforeCreate 與created 但是的執行在beforeCreate 與created之前,所以setup無法使用 data 和 methods 中的資料和方法,即無法操作this,setup中的this等於 undefined,又因為setup中建立的變數與方法最後都要通過return返回出去,所以setup中的程式只能是同步的,而不能是非同步,除非return 後面只接受一個非同步物件,物件返回setup內定義的變數與方法,然後父元件使用Suspense標籤包裹非同步元件;

vue3中 如果要使用vue2的beforeDestroy與destroyed需要把名稱分別改為beforeUnmount,unmounted

如果vue3中同時使用了vue2的寫法,vue3的寫法會優先執行;

簡單例子說明

父元件App.vue

<template>
  <h1>App父級元件</h1>
  <button @click="childShow = !childShow">切換child子元件的顯示</button>
  <hr />
  <child v-if="childShow" />
</template>
<script lang="ts">
import { defineComponent, reactive, ref } from "vue";
//引入子元件
import child from "./components/child.vue";
export default defineComponent({
  name: "App",
  components: {
    child,
  },
  setup() {
    const childShow = ref(true);
    return {
      childShow,
    };
  },
});
</script>
<style>
* {
  margin: 0;
  padding: 0;
}
</style>

子元件child.vue

<template>
  <h2>child 子級元件</h2>
  <h3>{{ name }}</h3>
  <button @click="updateName">更新name</button>
</template>
<script lang="ts">
import {
  defineComponent,
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted,
  ref,
} from "vue";
export default defineComponent({
  name: "child",
  //vue2中的生命週期勾點
  beforeCreate() {
    console.log("vue2 中的生命週期 beforeCreate");
  },
  created() {
    console.log("vue2 中的生命週期 created");
  },
  beforeMount() {
    console.log("vue2 中的生命週期 beforeMount");
  },
  mounted() {
    console.log("vue2 中的生命週期 mounted");
  },
  beforeUpdate() {
    console.log("vue2 中的生命週期 beforeUpdate");
  },
  updated() {
    console.log("vue2 中的生命週期 updated");
  },
  // vue2中的 beforeDestroy與 destroyed已經改名 無法使用
  beforeUnmount() {
    console.log("vue2 中的生命週期 beforeDestroy(beforeUnmount)");
  },
  unmounted() {
    console.log("vue2 中的生命週期 destroyed(unmounted)");
  },
  setup() {
    console.log("vue3中的setup");
    const name = ref("hhh");
    const updateName = () => {
      name.value += "6……6………6";
    };
    onBeforeMount(() => {
      console.log("vue3 中的生命週期 onBeforeMount");
    });
    onMounted(() => {
      console.log("vue3 中的生命週期 onMounted");
    });
    onBeforeUpdate(() => {
      console.log("vue3 中的生命週期 onBeforeUpdate");
    });
    onUpdated(() => {
      console.log("vue3 中的生命週期 onUpdated");
    });
    onBeforeUnmount(() => {
      console.log("vue3 中的生命週期 onBeforeUnmount");
    });
    onUnmounted(() => {
      console.log("vue3 中的生命週期 onUnmounted");
    });
    return {
      name,
      updateName,
    };
  },
});
</script>

執行起來的顯示效果

進入頁面 按f12 開啟偵錯 重新整理頁面

可以看出vue3中

  • setup執行在beforeCreate與created前面;
  • onBeforeMount執行在beforeMount前面;
  • onMounted執行在mounted前面;

點選 更新name

可以看出vue3中

  • onBeforeUpdate執行在beforeUpdate前面;
  • onUpdated執行在updated前面;
  • 點選 切換child子元件的顯示

可以看出vue3中

  • onBeforeUnmount執行在beforeDestroy前面;
  • onUnmounted執行在destroyed前面;

三種情況下的生命週期執行順序

生命週期:在建立一個vue範例時,會經歷一系列的初始化過程(Vue範例從建立到銷燬的過程),這個過程就是vue的生命週期。

Vue提供給開發者的一系列的回撥函數,方便我們新增自定義的邏輯,Vue的生命週期從建立到銷燬,重要的節點掛載資料更新。

  • 建立階段 beforeCreate、created
  • 掛載渲染頁面階段 beforeMount、mounted
  • 更新階段 beforeUpdate、updated
  • 解除安裝階段 beforeDestory、destoryed

1、單頁面下生命週期順序

獻上一波程式碼,看下各週期勾點函數的執行順序:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title>vue生命週期學習</title>
	<script src="https://cdn.bootcss.com/vue/2.4.2/vue.js"></script>
</head>
<body>
<div id="app">
	<h1>{{message}}</h1>
</div>
</body>
<script>
    var vm = new Vue({
        el: '#app',
        data: {
            message: 'Vue的生命週期'
        },
        beforeCreate: function() {
            console.group('------beforeCreate建立前狀態------');
            console.log("%c%s", "color:red" , "el     : " + this.$el); //undefined
            console.log("%c%s", "color:red","data   : " + this.$data); //undefined
            console.log("%c%s", "color:red","message: " + this.message)
        },
        created: function() {
            console.group('------created建立完畢狀態------');
            console.log("%c%s", "color:red","el     : " + this.$el); //undefined
            console.log("%c%s", "color:red","data   : " + this.$data); //已被初始化
            console.log("%c%s", "color:red","message: " + this.message); //已被初始化
        },
        beforeMount: function() {
            console.group('------beforeMount掛載前狀態------');
            console.log("%c%s", "color:red","el     : " + (this.$el)); //已被初始化
            console.log(this.$el);
            console.log("%c%s", "color:red","data   : " + this.$data); //已被初始化
            console.log("%c%s", "color:red","message: " + this.message); //已被初始化
        },
        mounted: function() {
            console.group('------mounted 掛載結束狀態------');
            console.log("%c%s", "color:red","el     : " + this.$el); //已被初始化
            console.log(this.$el);
            console.log("%c%s", "color:red","data   : " + this.$data); //已被初始化
            console.log("%c%s", "color:red","message: " + this.message); //已被初始化
        },
        beforeUpdate: function () {
            console.group('beforeUpdate 更新前狀態===============》');
            console.log("%c%s", "color:red","el     : " + this.$el.innerHTML);
            console.log(this.$el);
            console.log("%c%s", "color:red","data   : " + this.$data);
            console.log("%c%s", "color:red","message: " + this.message);
        },
        updated: function () {
            console.group('updated 更新完成狀態===============》');
            console.log("%c%s", "color:red","el     : " + this.$el.innerHTML);
            console.log(this.$el);
            console.log("%c%s", "color:red","data   : " + this.$data);
            console.log("%c%s", "color:red","message: " + this.message);
        },
        beforeDestroy: function () {
            console.group('beforeDestroy 銷燬前狀態===============》');
            console.log("%c%s", "color:red","el     : " + this.$el);
            console.log(this.$el);
            console.log("%c%s", "color:red","data   : " + this.$data);
            console.log("%c%s", "color:red","message: " + this.message);
        },
        destroyed: function () {
            console.group('destroyed 銷燬完成狀態===============》');
            console.log("%c%s", "color:red","el     : " + this.$el);
            console.log(this.$el);
            console.log("%c%s", "color:red","data   : " + this.$data);
            console.log("%c%s", "color:red","message: " + this.message)
        }
    })
</script>
</html>

(1)建立階段:初始化事件,進行資料的觀測

  • new Vue({}) 建立一個空的範例物件,這個物件上只有生命週期函數和一些預設事件
  • 在beforeCreate時,$el和data都未初始化
  • created 執行,完成了對data的初始化,通過編譯將 template 模板轉換成渲染函數( render ) ,執行渲染函數就可以得到一個虛擬節點樹(記憶體中)
  • 先檢查 template是否存在 如果存在模板編譯成render函數,沒有將外部html作為模板渲染。綜合排名優先順序:render函數選項 > template選項 > outer HTML.

(2)掛載階段

  • 為vue範例新增$el成員,替換掛載的DOM成員
  • 其中在beforeMount時,初始化el和data,但el和data,但el和data,但el還是使用{{message}}進行佔位
  • mounted執行時,將message的值進行渲染

(3)更新階段:觸發對應元件的重新渲染

  • data 被改變時觸發生命週期函數 beforeUpdate 執行,data是最新的,頁面還未更新(舊的頁面)
  • 根據最新的 data 重新渲染虛擬 DOM,並掛載到頁面上,完成 Model 到 View 的更新
  • updated 執行,此時 data 和頁面都是最新的

(4)銷燬階段

  • beforeDestroy勾點函數在範例銷燬之前呼叫。在這一步,範例仍然完全可用。
  • destroyed勾點函數在Vue 範例銷燬後呼叫。呼叫後,Vue 範例指示的所有東西都會解繫結,所有的事件監聽器會被移除,所有的子範例也會被銷燬。

2、父子、兄弟元件的生命週期順序

<template>
	<div class="father">
		<component-A class="son_A"></component-A>
		<component-B class="son_B"></component-B>
	</div>
</template>
// script部分同上程式碼,不多寫了。

主要可以從以下幾種情況分析:

(1)建立過程:

父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted

(2)元件的內部更新:

子元件的內部更新過程是:子beforeUpdate->子updated

同理父元件的內部更新過程也是:父beforeUpdate->父updated

(3)元件之間的更新:

當子元件使用emit修改父元件狀態時,剛好這個狀態又繫結在子元件的props上,更新過程是:父beforeUpdate->子beforeUpdate->子updated->父updated

(4)父子元件銷燬:

父元件被銷燬時子元件也同時被銷燬,銷燬的勾點過程是:父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

父子元件完整的生命週期圖如下所示:

  • 從上圖可以看出,在父兄子元件掛載前,各元件的範例已經初始化完成。
  • 子元件掛載完成後,父元件還未掛載。所以元件資料回顯的時候,在父元件mounted中獲取api的資料,子元件的mounted是拿不到的。
  • 仔細看看父子元件生命週期勾點的執行順序,會發現created這個勾點是按照從外內順序執行,所以回顯場景的解決方案是:在created中發起請求獲取資料,依次在子元件的created中會接收到這個資料。
  • Vue父子元件生命週期勾點的執行順序遵循:從外到內,然後再從內到外,不管巢狀幾層深,也遵循這個規律。

3、不同頁面跳轉時各頁面生命週期的執行順序

跳轉不同頁面和part2是相同的原理,從第一個頁面(index)跳轉到下一個頁面(secondIndex)時,回先初始化secondIndex,之後在執行index頁面的銷燬階段,最後secondIndex掛載完成.

題外話:

在開發過程中,通過對整個生命週期的瞭解,就可以很清晰地知道可以在什麼階段做什麼事,或者某一操作應該在什麼階段執行

例如在create中進行資料操作,在mounted中進行DOM完成後的操作,在destroyed進行事件解綁和功能登出

當然,對於元件間、不同頁面跳轉的生命週期順序也應該更加了解,避免頁面渲染資料錯誤或根本拿不到資料等情況

總結來說就是在合適的時機做對的事情,才能事半功倍嘛~

以上為個人經驗,希望能給大家一個參考,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com