引起好奇的原因:
在学习vue2的过程中,发现好几个博文对这个生命周期触发的说的都很矛盾,并且没有看到具体的,打开官网一看也没有看到有关对这两个生命周期函数的具体说明,只是区分了v-if是对DOM元素的修改,频繁切换不适合使用,v-show则是对css的改变,频繁切换比较适合。但是初始渲染时v-show会加载,而v-if如果为false则只会在变为true的时候加载。不过这也解释了为什么子组件中会调用create和mount系列的四个钩子。
指正
感谢评论区@不叫猫先生的指正,原先我只是区分了vue2和vue3的环境来测试的,在vue3中我依旧使用的option API,因为vue3兼容vue2的生命周期钩子,所以测试代码是能够运行。
但实际上vue3在setup中使用composition API,因此我再次测试了在setup中生命周期钩子的情况,其实是在兼容的钩子前面加入了on。具体差异往下看。
对比普通变量
vue2中,不论是v-if还是v-show包裹普通变量或者文本都只会触发父组件的beforeUpdate和updated生命周期钩子函数。
vue3中也是一样。
对比组件的更新
与普通的变量大有不同,首先vue3的生命周期钩子函数不是beforeDestroy和destroyed,而是变成了beforeUnmount和unmounted。然后具体差别往下看:
先说结论
主要遵循几个原则:
- vue中的
setup中,主要是没有了create的两个钩子,而是替换为setup钩子。由于兼容性,其实更新顺序相同,只是生命周期钩子在vue2的基础上加了on前缀。 - 凡是子组件变化,都会导致父组件变化,并且父组件先于子组件变化,并且后于子组件结束。也就是说父组件包裹子组件的样子。
v-show的更新其实是添加display:none,所以只触发子组件的undate系列钩子,并且初始化时,就已经放置在文档流中,因此在切换v-show时不涉及mount系列钩子。但vue2和vue3对子组件的更新有所不同,vue3更加精细。v-if的更新是涉及了组件是否挂载的,因此除了触发父组件的update系列钩子,还会触发挂载和卸载的过程。(vue2使用destory系列卸载,而vue3使用unmount系列卸载)
下面是详细的变化流程:
vue2中,v-if包裹子组件
- 从false转为true:父
beforeUpdate、子beforeCreate、created、beforeMount、mounted、父updated。 - 从true转为false:父
beforeUpdate、子beforeDestroyed、destroyed、父updated。
vue3中,v-if包裹子组件
- 从false转为true:父
beforeUpdate、子beforeCreate、created、beforeMount、mounted、父updated。(与vue2相同) - 从true转为false:父
beforeUpdate、子beforeUnmount、unmounted、父updated。(这里在测试过程中,失去了created,因为vue3中的setup替换了create)
vue3的Setup中,v-if包裹子组件
- 从false转为true:父
onBeforeUpdate、子setup、onBeforeMount、onMounted、父onUpdated。 - 从true转为false:父
onBeforeUpdate、子onBeforeUnmount、onUnmounted、父onUpdated。
vue2中,v-show包裹子组件
- 从false转为true:父
beforeUpdate、父updated。 - 从true转为false:父
beforeUpdate、父updated。
vue3中,v-show包裹子组件
- 从false转为true:父
beforeUpdate、子beforeUpdate、updated、父updated。触发子组件updated。 - 从true转为false:父
beforeUpdate、子beforeUpdate、updated、父updated。触发子组件updated。
vue3的Setup中,v-show包裹子组件
- 从false转为true:父
beforeUpdate、子beforeUpdate、updated、父updated。触发子组件updated。 - 从true转为false:父
onBeforeUpdate、子onBeforeUpdate、onUpdated、父onUpdated。触发子组件onUpdated。
vue2测试代码
// vue2的测试代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue 测试实例 - 菜鸟教程(runoob.com)</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.7.14"></script>
</head>
<body>
<div id="app">
<p>{{ message }}</p>
<Comp v-if="awesome"></Comp>
<Comp1 v-show="awesome2"></Comp1>
<button @click="change">click here!</button>
</div>
<script>
Vue.component('comp1', {
template: '<div id="app"><p>Comp1</p></div>',
data() {
return {
}
},
beforeCreate() {
console.log("Comp1:", "beforeCreate");
},
created() {
console.log("Comp1:", "create");
},
beforeMount() {
console.log("Comp1:", "beforeMount");
},
mounted() {
console.log("Comp1:", "Mounted");
},
beforeUpdate() {
console.log("Comp1:", "beforeUpdate");
},
updated() {
console.log("Comp1:", "updated");
},
beforeDestroy() {
console.log("Comp1:", "beforeDestroy");
},
destroyed() {
console.log("Comp1:", "destroyed");
}
})
Vue.component('comp', {
template: '<div id="app"><p>Comp</p></div>',
data() {
return {
}
},
beforeCreate() {
console.log("Comp:", "beforeCreate");
},
created() {
console.log("Comp:", "create");
},
beforeMount() {
console.log("Comp:", "beforeMount");
},
mounted() {
console.log("Comp:", "Mounted");
},
beforeUpdate() {
console.log("Comp:", "beforeUpdate");
},
updated() {
console.log("Comp:", "updated");
},
beforeDestroy() {
console.log("Comp:", "beforeDestroy");
},
destroyed() {
console.log("Comp:", "destroyed");
}
})
new Vue({
el: '#app',
data: {
message: "Hello Vue!",
awesome: false,
awesome2: false
},
methods: {
change() {
this.awesome = !this.awesome;
this.awesome2 = !this.awesome2;
console.log("v-if:", this.awesome);
console.log("v-show:", this.awesome2);
}
},
beforeCreate() {
console.log("beforeCreate");
},
created() {
console.log("create");
},
beforeMount() {
console.log("beforeMount");
},
mounted() {
console.log("Mounted");
},
beforeUpdate() {
console.log("beforeUpdate");
},
updated() {
console.log("updated");
},
beforeUnmount() {
console.log("beforeUnmount");
},
unmounted() {
console.log("unmounted");
}
})
</script>
</body>
</html>
console.log:
Vue3测试代码
在Vue SFC Playground 在线测试的。
- App.vue
// App.vue
<script>
import Comp from './Comp.vue';
import Comp1 from './Comp1.vue';
export default {
data() {
return {
msg: "Hello World!",
message: "Hello Vue!",
awesome: false,
awesome2: false
};
},
methods: {
change() {
this.awesome = !this.awesome;
this.awesome2 = !this.awesome2;
console.log("v-if:", this.awesome);
console.log("v-show:", this.awesome2);
}
},
beforeCreate() {
console.log("beforeCreate");
},
created() {
console.log("create");
},
beforeMount() {
console.log("beforeMount");
},
mounted() {
console.log("Mounted");
},
beforeUpdate() {
console.log("beforeUpdate");
},
updated() {
console.log("updated");
},
beforeUnmount() {
console.log("beforeUnmount");
},
unmounted() {
console.log("unmounted");
},
components: { Comp, Comp1 }
}
</script>
<template>
<div id="app">
<p>{{ message }}</p>
<Comp v-if="awesome"></Comp>
<Comp1 v-show="awesome2"></Comp1>
<button @click="change">click here!</button>
</div>
</template>
- Comp.vue
// Comp.vue
<script>
export default {
data() {
return {
}
},
beforeCreate() {
console.log("Comp-v-if:", "beforeCreate");
},
created() {
console.log("Comp-v-if:", "create");
},
beforeMount() {
console.log("Comp-v-if:", "beforeMount");
},
mounted() {
console.log("Comp-v-if:", "Mounted");
},
beforeUpdate() {
console.log("Comp-v-if:", "beforeUpdate");
},
updated() {
console.log("Comp-v-if:", "updated");
},
beforeUnmount() {
console.log("Comp-v-if:","beforeUnmount");
},
unmounted() {
console.log("Comp-v-if:","unmounted");
},
}
</script>
<template>
<div id="app">
<p>Comp</p>
</div>
</template>
- Comp1.vue
<script>
export default {
data() {
return {
}
},
beforeCreate() {
console.log("Comp1-v-show:", "beforeCreate");
},
created() {
console.log("Comp1-v-show:", "create");
},
beforeMount() {
console.log("Comp1-v-show:", "beforeMount");
},
mounted() {
console.log("Comp1-v-show:", "Mounted");
},
beforeUpdate() {
console.log("Comp1-v-show:", "beforeUpdate");
},
updated() {
console.log("Comp1-v-show:", "updated");
},
beforeUnmount() {
console.log("Comp1-v-show:","beforeUnmount");
},
unmounted() {
console.log("Comp1-v-show:","unmounted");
},
}
</script>
<template>
<div id="app">
<p>Comp1</p>
</div>
</template>
console.log:
Vue3 Setup 测试代码
- App.vue
<script setup>
import { ref } from 'vue'
import Comp from './Comp.vue';
import Comp1 from './Comp1.vue';
import { onBeforeMount, onMounted,
onBeforeUpdate, onUpdated,
onBeforeUnmount, onUnmounted } from 'vue';
console.log('App:setup')
onBeforeMount(() => console.log('App:'+'onBeforeMount'));
onMounted(() => console.log('App:'+'onMounted'));
onBeforeUpdate(() => console.log('App:'+'onBeforeUpdate'));
onUpdated(() => console.log('App:'+'onUpdated'));
onBeforeUnmount(() => console.log('App:'+'onBeforeUnmount'));
onUnmounted(() => console.log('App:'+'onUnmounted'));
const message = ref('Hello World!')
let awesome = ref(true), awesome2 = ref(true);
const change = () => {
awesome.value = !awesome.value;
awesome2.value = !awesome2.value;
console.log("v-if:", awesome.value);
console.log("v-show:", awesome2.value);
}
</script>
<template>
<div id="app">
<p>{{ message }}</p>
<Comp v-if="awesome"></Comp>
<Comp1 v-show="awesome2"></Comp1>
<button @click="change">click here!</button>
</div>
</template>
- Comp-v-if.vue
<script setup>
import { onBeforeMount, onMounted,
onBeforeUpdate, onUpdated,
onBeforeUnmount, onUnmounted } from 'vue';
console.log('Comp-v-if:setup')
onBeforeMount(() => console.log('Comp-v-if:'+'onBeforeMount'));
onMounted(() => console.log('Comp-v-if:'+'onMounted'));
onBeforeUpdate(() => console.log('Comp-v-if:'+'onBeforeUpdate'));
onUpdated(() => console.log('Comp-v-if:'+'onUpdated'));
onBeforeUnmount(() => console.log('Comp-v-if:'+'onBeforeUnmount'));
onUnmounted(() => console.log('Comp-v-if:'+'onUnmounted'));
</script>
<template>
<div id="app">
<p>Comp</p>
</div>
</template>
- Comp1-v-show.vue
<script setup>
import { onBeforeMount, onMounted,
onBeforeUpdate, onUpdated,
onBeforeUnmount, onUnmounted } from 'vue';
console.log('Comp-v-show:setup')
onBeforeMount(() => console.log('Comp-v-show:'+'onBeforeMount'));
onMounted(() => console.log('Comp-v-show:'+'onMounted'));
onBeforeUpdate(() => console.log('Comp-v-show:'+'onBeforeUpdate'));
onUpdated(() => console.log('Comp-v-show:'+'onUpdated'));
onBeforeUnmount(() => console.log('Comp-v-show:'+'onBeforeUnmount'));
onUnmounted(() => console.log('Comp-v-show:'+'onUnmounted'));
</script>
<template>
<div id="app">
<p>Comp1</p>
</div>
</template>
运行结果: