深入瞭解el-dialog組件中插槽的生命週期

問題表現

在項目中使用el-dialog彈框組件時,有個開發任務需要根據生命週期向插槽內的子組件傳遞數據。但是詭異的發現el-dialog這個組件中插槽父子組件的聲明週期不是按照:

父組件beforeCreate→父組件created→父組件beforeMounted→子組件created→子組件beforeMounted→子組件mounted→父組件mounted

而是以下順序:

父組件beforeCreate→父組件created→父組件beforeMounted→父組件mounted→子組件created→子組件beforeMounted→子組件mounted

此時我的內心:
在這裏插入圖片描述

定位問題

  1. 第一步:是不是插槽本身影響父子組件的生命週期
<!-- 父組件 -->
<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>

控制檯輸出如下:
在這裏插入圖片描述
那就不是插槽的問題。(原諒菜雞的我居然敢懷疑框架本身)

  1. 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-showv-if條件對父子組件生命週期的影響,得出一下規則:

v-if

  1. show的默認值爲true,執行順序符合父子組件生命週期,即:

父組件beforeCreate→父組件created→父組件beforeMounted→子組件created→子組件beforeMounted→子組件mounted→父組件mounted

  1. show的默認值爲false,在父組件的createdbeforeMount生命週期將show變爲true,執行順序父子組件生命週期。

  2. show的默認值爲false,在父組件的mounted生命週期將show變爲true,執行順序將會變爲:

父組件beforeCreate→父組件created→父組件beforeMounted→父組件mounted→父組件beforeUpdated→子組件created→子組件beforeMounted→子組件mounted

v-show

無論上述哪種情況,都符合父子組件的聲明週期,因爲v-show本身就會掛載,只是通過display來顯示與否。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章