动态组件
使用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 这两个生命周期钩子函数将会被对应执行。
| activated | deactivated |
|---|---|
| 被 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进行下一步处理。
- 第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})