問題表現
在項目中使用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
來顯示與否。