Vue 异步组件/动态组件

181

问题:动态组件component中切换异步组件,两个组件之间的生命周期执行顺序在首次加载和二次加载的时候存在差异。

<template>
    <div>
        <button @click="handleChange">改变</button>
        <component :is="activeTab" />  
    </div>
</template>
<script>
  export default {
      data () {
        return {
          activeIndex: '',
          activeTab: null,
          tabs: [
            {
              name: 'tab1',
              component: () => import('./tab1')
            },
            {
              name: 'tab2',
              component: () => import('./tab2')
            }
          ]
        }
      },

      created () {
        this.activeIndex = 0
        this.activeTab = this.tabs[this.activeIndex].component
      },

      methods: {
        handleChange () {
          this.activeIndex = (this.activeIndex + 1) % 2
          this.activeTab = this.tabs[this.activeIndex].component
        }
      },

      /* components: {
        tab1: {
          template: `<div>tab1</div>`,
          beforeCreate() {
            console.log('tab1:beforeCreate')
          },
          created() {
            console.log('tab1:created')
          },
          mounted() {
            console.log('tab1:mounted')
          },
          beforeDestroy() {
            console.log('tab1:beforeDestroy')
          },
          destroyed() {
            console.log('tab1:destroyed')
          }
        },
        tab2: {
          template: `<div>tab2</div>`,
          beforeCreate() {
            console.log('tab2:beforeCreate')
          },
          created() {
            console.log('tab2:created')
          },
          mounted() {
            console.log('tab2:mounted')
          },
          beforeDestroy() {
            console.log('tab2:beforeDestroy')
          },
          destroyed() {
            console.log('tab2:destroyed')
          }
        }
      } */
    }
 </script>

情景一:先展示tab1,然后切换tab2(首次加载)。生命周期先执行 tab1 的 beforeDestroy、destroyed,后执行 tab2 的 beforeCreate、created、beforeMounted

情景二:切换回tab1,再次切换tab2(二次加载)。生命周期先执行 tab2 的 beforeCreate、created、beforeMounted,然后才执行 tab1 的 beforeDestroy、destroyed,最后执行 tab2 的 mounted

原因:源码 createComponent 先对动态组件渲染进行判断,如果是 Vue.component 组件,则直接渲染,所以生命周期会按照情景二的执行顺序 ;但如果是异步组件,需要解析异步组件,所以先返回一个Placeholder占位组件,继续其他内容的渲染,进程就会先执行 tab1 的销毁钩子,等到异步组件解析完,替换Placeholder占位组件,并执行tab2的钩子(情景一)。再次切换有差异,主要就是第一次解析异步组件有对解析结果进行缓存,所以第二次重新渲染的时候,就可以直接返回缓存的解析内容。

createComponent

createComponent.png

resolveAsyncComponent

resolveAsyncComponent.png