【Vue基础】-动态组件和异步组件

392 阅读4分钟

动态组件

使用keep-alive标签:如果把切换出去的组件保留在内存中,则可以保留它的状态或避免重新渲染,为此Vue官网给到的解释:动态组件keep-alive

组件实例在需要缓存的时候需可以使用动态组件:用

...
<!-- 失活的组件将会被缓存!-->
<keep-alive>
    <component :is="liveCpn"></component>
    //写法等同于: <child-cpn></child-cpn>
</keep-alive>
...

...
components: { childCpn },
data(){
    return{
        liveCpn:'childCpn',
    }
}
...

Tips: 上面代码中的liveCpn可以用计算属性来动态改变值,比如根据不同按钮显示不同组件,如: computed: { curCpn() { if (this.choiceName == "twoBtn") { return "twoCpn"; } else { return "oneCpn"; } }, },即用curCpn替换liveCpn。

当组件在 <keep-alive> 内被切换,它的 activated 和 deactivated 这两个生命周期钩子函数将会被对应执行。

activateddeactivated
被 keep-alive 缓存的组件激活时调用被 keep-alive 缓存的组件失活时调用
  // oneCpn组件的钩子函数中的activated和deactivated
  activated() {
    console.log("组件激活了");
  },
  deactivated() {
    console.log("组件失活了");
  },

include(包括) and exclude(不包括)

//数组写法
<keep-alive :include="['oneCpn']">
    <component :is="liveCpn"></component>
</keep-alive>

//分隔符写法
<keep-alive :include="'oneCpn'">
    <component :is="curCpn"></component>
</keep-alive>

//正则表达式写法
<keep-alive :include="/oneCpn|twoCpn/">
    <component :is="curCpn"></component>
</keep-alive>

// exclude和include用法一样,只是结果不同

Tips: include后面接的跟bind绑定值的原理一样,传入的是一个字符串,而不是组件

max:注意max传的值大于0才生效

<keep-alive :exclude="/twoCpn/" :max="1">
        <component :is="curCpn"></component>
</keep-alive>

页面时周期执行顺序的研究:

借鉴Vue官网关于这个动态组件的实例,我理解的图解:

graph TD
TabA -->componentTotal--> componentA
componentA-->btnA+conotentA
componentA-->btnB+conotentB
graph TD
TabB -->componentTotal--> componentB

父组件用普通字符串、数组或者正则,子组件缓存成功的情况下:

父子组件初始化:看父子组件打印的结果

执行顺序,如下:
index.vue?0dbc:45 父组件:beforeCreate
index.vue?0dbc:48 父组件:created
index.vue?0dbc:51 父组件:beforeMount
index.vue?0dbc:70 父组件:执行了计算属性
oneCpn.vue?92cd:33 子组件A:beforeCreate
oneCpn.vue?92cd:36 子组件A:created
oneCpn.vue?92cd:39 子组件A:beforeMount
oneCpn.vue?92cd:65 子组件A:执行了计算属性
oneCpn.vue?92cd:42 子组件A:mounted
oneCpn.vue?92cd:45 子组件A:activated
index.vue?0dbc:54 父组件:mounted

点击子组件conotentA:看父子组件打印的结果

oneCpn.vue?92cd:51 子组件A:beforeUpdate
oneCpn.vue?92cd:54 子组件A:updated

点击TabB:看父子组件打印的结果

index.vue?0dbc:57 父组件:beforeUpdate
index.vue?0dbc:70 父组件:执行了计算属性
oneCpn.vue?92cd:48 子组件A:deactivated
index.vue?0dbc:60 父组件:updated

Tips: 可以看出,activated的执行是在子组件A的mounted之后才执行computed的执行时间发生在beforeMount和mounted之间<keep-alive> 包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。因为并没有执行子组件beforeDestroy和destroyed的生命周期钩子函数的执行结果。

父组件用普通字符串、数组或者正则,子组件缓存失败的情况下:

父子组件初始化:看父子组件打印的结果

执行顺序,如下:
index.vue?0dbc:45 父组件:beforeCreate
index.vue?0dbc:48 父组件:created
index.vue?0dbc:51 父组件:beforeMount
index.vue?0dbc:70 父组件:执行了计算属性
oneCpn.vue?92cd:33 子组件A:beforeCreate
oneCpn.vue?92cd:36 子组件A:created
oneCpn.vue?92cd:39 子组件A:beforeMount
oneCpn.vue?92cd:65 子组件A:执行了计算属性
oneCpn.vue?92cd:42 子组件A:mounted
index.vue?0dbc:54 父组件:mounted

点击子组件conotentA:看父子组件打印的结果

oneCpn.vue?92cd:51 子组件A:beforeUpdate
oneCpn.vue?92cd:54 子组件A:updated

点击TabB:看父子组件打印的结果

index.vue?0dbc:57 父组件:beforeUpdate
index.vue?0dbc:70 父组件:执行了计算属性
oneCpn.vue?92cd:57 子组件A:beforeDestroy
oneCpn.vue?92cd:60 子组件A:destroyed
index.vue?0dbc:60 父组件:updated

Tips: beforeDestroy和destroyed一起执行的结果替换了deactivated,所以deactivated的执行类似于beforeDestroy和destroyed的执行,只是"destroyed"强调销毁,而"deactivated"强调失活

父组件用计算属性代替字符串,子组件缓存的情况:

点击TabB:看父子组件打印的结果

index.vue?0dbc:57 父组件:beforeUpdate
index.vue?0dbc:91 父组件:执行了计算属性
oneCpn.vue?92cd:48 子组件A:deactivated
oneCpn.vue?92cd:57 子组件A:beforeDestroy
oneCpn.vue?92cd:60 子组件A:destroyed
index.vue?0dbc:81 父组件:updated

而之前描述的正常缓存情况下的结果

index.vue?0dbc:57 父组件:beforeUpdate
index.vue?0dbc:119 父组件:执行了计算属性
oneCpn.vue?92cd:48 子组件A:deactivated
index.vue?0dbc:109 父组件:updated

Tips: 可以发现computed的缓存和keep-alive的缓存是不一样的,计算属性computed是值改变时才缓存,而<keep-alive></keep-alive>是强制缓存。当 <keep-alive :include="oneCpn"><component :is="curCpn"></component></keep-alive>中的"curCpn"是计算属性时会发生,beforeDestroy和destroyed的生命周期钩子函数。但如果是没用计算属性就不会销毁生命周期!!!

异步组件

某些应用场景中,加载组件时同步渲染页面,如果组件内容特别大很费系统资源时,考虑按需加载组件也就是异步组件,它会把结果缓存起来供未来重渲染,即初始化一次后面用缓存的内容。 异步组件的具体使用:

  • 第1种使用方法
...
  components: {
    sonCpn: () =>
      import("./cpn/sonCpn.vue")  //这里就可以理解为一个异步组件
  },
...

初次点击按钮可以看到Network中,会有一个另外的1.js文件出现,是因为使用import()异步加载模块来实现分包操作产生的。import函数的返回值是一个Promise,所以我们也可以使用then进行下一步处理。 4c44369bc531d4fa79eb87b5783d303.png

  • 第2种使用方法
...
    import loadingCpn from "./cpn/loadingCpn.vue";
    // 在script标签中使用吧变量引入这个工厂函数
    const AsyncCpn = () => ({
      component: import("./cpn/sonCpn.vue"),
      loading: loadingCpn,
      // delay: 10000,
      // timeout:2000
    });
...

Tips:在箭头函数中返回一个对象时要加():const add = () =>{a:1,b:2}会报错,改成const add = () =>({a:1,b:2})