问题表现
在项目中使用el-dialog弹框组件时,有个开发任务需要根据生命周期向插槽内的子组件传递数据。但是诡异的发现el-dialog这个组件中插槽父子组件的声明周期不是按照:
父组件
beforeCreate
→父组件created
→父组件beforeMounted
→子组件created
→子组件beforeMounted
→子组件mounted
→父组件mounted
而是以下顺序:
父组件
beforeCreate
→父组件created
→父组件beforeMounted
→父组件mounted
→子组件created
→子组件beforeMounted
→子组件mounted
此时我的内心:
定位问题
- 第一步:是不是插槽本身影响父子组件的生命周期
<!-- 父组件 -->
<template>
<h1>
<slot></slot>
</h1>
</template>
<script>
import Child from './Child'
export default {
name: 'Parent',
components: {
Child
},
beforeCreate () {
console.log('fu-beforeCreate')
},
created () {
console.log('fu-created')
},
beforeMount () {
console.log('fu-beforeMount')
},
mounted () {
console.log('fu-mounted')
}
}
</script>
<!-- 子组件 -->
<template>
<span>
随风丶逆风
</span>
</template>
<script>
export default {
name: 'Child',
beforeCreate () {
console.log('zi-beforeCreate')
},
created () {
console.log('zi-created')
},
beforeMount () {
console.log('zi-beforeMount')
},
mounted () {
console.log('zi-mounted')
}
}
</script>
控制台输出如下:
那就不是插槽的问题。(原谅菜鸡的我居然敢怀疑框架本身)
- elementUI组件的问题
遂直接查看Dialog的源码,省略不必要的地方,大致如下:
<template>
<transition
name="dialog-fade"
@after-enter="afterEnter"
@after-leave="afterLeave">
<div
v-show="visible"
class="el-dialog__wrapper"
@click.self="handleWrapperClick">
<div>
<!-- 这地方是关键之一,用了v-if -->
<div class="el-dialog__body" v-if="rendered"><slot></slot></div>
<div class="el-dialog__footer" v-if="$slots.footer">
<slot name="footer"></slot>
</div>
</div>
</div>
</transition>
</template>
<script>
export default {
// 关键地方之二,父组件在mounted之后才修改rendered状态
mounted() {
if (this.visible) {
this.rendered = true;
this.open();
if (this.appendToBody) {
document.body.appendChild(this.$el);
}
}
}
}
</script>
因为在父组件mounted
之后才修改条件,所以子组件的生命周期在父组件mounted
之后。
复现问题
复现一下代码,修改父组件Parent的插槽,添加v-if
条件判断,然后在mounted
的阶段将条件置为true
,控制台输出就和el-dialog一样了。
<!-- 父组件 -->
<template>
<h1>
<slot v-if="show"></slot>
</h1>
</template>
<script>
import Child from './Child'
export default {
name: 'Parent',
components: {
Child
},
data () {
return {
show: false
}
},
beforeCreate () {
console.log('fu-beforeCreate')
},
created () {
console.log('fu-created')
},
beforeMount () {
console.log('fu-beforeMount')
},
mounted () {
this.show = true
console.log('fu-mounted')
}
}
</script>
拓展一下
发现这个问题之后,随之想到了v-show
,于是测试不同阶段修改v-show
和v-if
条件对父子组件生命周期的影响,得出一下规则:
v-if
- 当
show
的默认值为true
,执行顺序符合父子组件生命周期,即:
父组件
beforeCreate
→父组件created
→父组件beforeMounted
→子组件created
→子组件beforeMounted
→子组件mounted
→父组件mounted
-
当
show
的默认值为false
,在父组件的created
或beforeMount
生命周期将show
变为true
,执行顺序父子组件生命周期。 -
当
show
的默认值为false
,在父组件的mounted
生命周期将show
变为true
,执行顺序将会变为:
父组件
beforeCreate
→父组件created
→父组件beforeMounted
→父组件mounted
→父组件beforeUpdated
→子组件created
→子组件beforeMounted
→子组件mounted
v-show
无论上述哪种情况,都符合父子组件的声明周期,因为v-show
本身就会挂载,只是通过display
来显示与否。