Vue父子组件生命周期执行顺序


注意:本文档基于 Vue 2.x 版本编写。在 Vue 3 中,部分生命周期钩子名称已变更:

  • beforeDestroybeforeUnmount
  • destroyedunmounted

Vue 3 的生命周期请参考官方文档:https://cn.vuejs.org/guide/essentials/lifecycle.html

1.挂载阶段(初始化相关属性)

  1. beforeCreate
  2. created
  3. beforeMount
  4. mounted

执行顺序为:
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted

一定得等子组件挂载完毕后,父组件才能挂在完毕,所以父组件的 mounted 在最后。

2.更新阶段(元素或组件的变更操作)

  1. beforeUpdate
  2. updated

注意:当父子组件有数据传递时,才有这个更新阶段执行顺序的比较。

执行顺序为:
父beforeUpdate -> 子beforeUpdate -> 子updated -> 父updated

3.销毁阶段(销毁相关属性)

  1. beforeDestroy
  2. destroyed

执行顺序为:
父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed

结合父子组件之后,一个完整的父子组件生命周期:

父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted->父beforeUpdate->子beforeUpdate->子updated->父updated->父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

4.总结

Vue 父子组件生命周期钩子的执行顺序遵循:从外到内,再从内到外。

上代码

<!-- html部分 -->
<div id="app">
    <h1>app根组件</h1>
    <hr />
    <father></father>
</div>
<!-- 父组件 -->
<template id="father">
    <div>
        <h1>父组件</h1>
        <button id="destroy" @click="btnDestroy">销毁组件</button>
        <button id="update" @click="btnUpdate">更新组件</button>
        <hr />
        <child :msg="message"></child>
    </div>
</template>
<!-- 子组件 -->
<template id="child">
    <div>
        <h1>子组件</h1>
        <h3>{{msg}}</h3>
        <h3>{{childMsg}}</h3>
    </div>
</template>
// js部分
var child = {
    template: "#child",
    props: ["msg"],
    data() {
        return {
            childMsg: "子组件自己的信息",
        };
    },
    watch: {
        msg: {
            handler(newVal, oldVal) {
                console.log("子组件————watch msg变化:", newVal, oldVal);
            },
            immediate: true, // 立即执行
        },
        childMsg: {
            handler(newVal, oldVal) {
                console.log("子组件————watch childMsg变化:", newVal, oldVal);
            },
            // 未设置immediate,不会立即执行
        },
    },
    beforeCreate() {
        console.log("子组件————beforeCreate...");
    },
    created() {
        console.log("子组件————create...");
    },
    beforeMount() {
        console.log("子组件————beforeMount...");
    },
    mounted() {
        console.log("子组件————mounted...");
        // 在mounted中修改childMsg,触发非immediate的watch
        this.childMsg = "子组件自己的信息-修改后";
    },
    beforeUpdate() {
        console.log("子组件————beforeUpdate...");
    },
    updated() {
        console.log("子组件————updated...");
    },
    beforeDestroy() {
        console.log("子组件————beforeDestroy...");
    },
    destroyed() {
        console.log("子组件————destroyed...");
    },
};

Vue.component("father", {
    template: "#father",
    data() {
        return {
            message: "来自father组件的信息",
        };
    },
    components: {
        child,
    },
    watch: {
        message: {
            handler(newVal, oldVal) {
                console.log("父组件————watch message变化:", newVal, oldVal);
            },
            immediate: true, // 立即执行
        },
    },
    beforeCreate() {
        console.log("父组件————beforeCreate...");
    },
    created() {
        console.log("父组件————create...");
    },
    beforeMount() {
        console.log("父组件————beforeMount...");
    },
    mounted() {
        console.log("父组件————mounted...");
    },
    beforeUpdate() {
        console.log("父组件————beforeUpdate...");
    },
    updated() {
        console.log("父组件————updated...");
    },
    beforeDestroy() {
        console.log("父组件————beforeDestroy...");
    },
    destroyed() {
        console.log("父组件————destroyed...");
    },
    methods: {
        btnDestroy() {
            //完全销毁一个实例。清理它与其它实例的连接,解绑它的全部指令及事件监听器。
            //触发 beforeDestroy 和 destroyed 的钩子。
            // https://cn.vuejs.org/v2/api/#vm-destroy
            this.$destroy();
            console.log("销毁父子组件");
        },
        btnUpdate() {
            this.message = "变更父组件的信息";
            console.log(this.message);
        },
    },
});

var vm = new Vue({
    el: "#app",
    beforeCreate() {
        console.log("app根组件————beforeCreate...");
    },
    created() {
        console.log("app根组件————create...");
    },
    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...");
    },
});

5.补充:

页面A到页面B跳转的时候,发生了什么?

首先是先B组件先created然后beforeMount接着A组件才被销毁,A组件才执行beforeDestory,以及destoryed,最后B组件才开始mounted

即:B页面created>B页面beforeMount>A页面beforeDestory>A页面destoryed>B页面mounted

5.1 watch执行时机

官网的生命周期图中, init reactivity 是晚于 beforeCreate 但是早于 created 的。
watch 加了 immediate: true,应当同 init reactivity 周期一同执行,会早于 created 执行。
而正常的 watch 执行,则是 mounted 周期后触发 data changes 的周期执行,晚于 created 执行。

watch 设置 immediate:true 的情况下
watch 先执行,mounted 后执行

代码示例说明

在上面的代码示例中,我们添加了以下watch配置:

  1. 父组件:添加了对 message 属性的watch,设置了 immediate: true
  2. 子组件
    • 添加了对 msg 属性的watch,设置了 immediate: true
    • 添加了对 childMsg 属性的watch,未设置 immediate
    • mounted 钩子中修改了 childMsg 的值,以触发非immediate的watch

执行顺序会是:

  • 父组件beforeCreate → 父组件watch message(immediate: true) → 父组件created → 父组件beforeMount → 子组件beforeCreate → 子组件watch msg(immediate: true) → 子组件created → 子组件beforeMount → 子组件mounted → 子组件修改childMsg → 子组件beforeUpdate → 子组件watch childMsg → 子组件updated → 父组件mounted

这样可以清晰地看到:

  • 带有 immediate: true 的watch会在 created 之前执行
  • 不带 immediate 的watch会在数据变化时执行,晚于 mounted

文章作者: 弈心
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 弈心 !
评论
  目录