深入了解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来显示与否。

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