1️⃣ 动态组件
有的时候,在不同组件之间进行动态切换是非常有用的,比如在一个多标签的界面里:
1️⃣ 动态组件的使用示例
上述内容可以通过 Vue 的
1️⃣ 动态组件缓存
你会注意到,如果你选择了一篇文章,切换到 Archive 标签,然后再切换回 Posts,是不会继续展示你之前选择的文章的。这是因为你每次切换新标签的时候,Vue 都创建了一个新的 currentTabComponent
实例。
重新创建动态组件的行为通常是非常有用的,但是在这个案例中,我们更希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来。为了解决这个问题,我们可以用一个
注意这个
要求被切换到的组件都有自己的名字,不论是通过组件的 name 选项还是局部/全局注册。
2️⃣ keep-alive
keep-alive 组件是 vue 的内置组件,用于缓存内部组件实例。这样做的目的在于,keep-alive 内部的组件切回时,不用重新创建组件实例,而直接使用缓存中的实例,一方面能够避免创建组件带来的开销,另一方面可以保留组件的状态。
2️⃣ include & exclude & max
keep-alive 具有 include 和 exclude 属性,通过它们可以控制哪些组件进入缓存。另外它还提供了 max 属性,通过它可以设置最大缓存数,当缓存的实例超过该数时,vue 会移除最久没有使用的组件缓存。
2️⃣ activated & deactivated
受 keep-alive 的影响,其内部所有嵌套的组件都具有两个生命周期钩子函数,分别是 activated
和 deactivated
,它们分别在组件激活和失活时触发。第一次 activated
触发是在 mounted
之后
1. **activated:**keep-alive 组件激活时调用。
2. **deactivated:**keep-alive 组件停用时调用。
在具体的实现上,keep-alive 在内部维护了一个 key 数组和一个缓存对象
// keep-alive 内部的声明周期函数
created () {
this.cache = Object.create(null)
this.keys = []
}
key 数组记录目前缓存的组件 key 值,如果组件没有指定 key 值,则会为其自动生成一个唯一的 key 值
cache 对象以 key 值为键,vnode 为值,用于缓存组件对应的虚拟 DOM
在 keep-alive 的渲染函数中,其基本逻辑是判断当前渲染的 vnode 是否有对应的缓存,如果有,从缓存中读取到对应的组件实例;如果没有则将其缓存。
当缓存数量超过 max 数值时,keep-alive 会移除掉 key 数组的第一个元素
render(){
const slot = this.$slots.default; // 获取默认插槽
const vnode = getFirstComponentChild(slot); // 得到插槽中的第一个组件的vnode
const name = getComponentName(vnode.componentOptions); //获取组件名字
const { cache, keys } = this; // 获取当前的缓存对象和key数组
const key = ...; // 获取组件的key值,若没有,会按照规则自动生成
if (cache[key]) {
// 有缓存
// 重用组件实例
vnode.componentInstance = cache[key].componentInstance
remove(keys, key); // 删除key
// 将key加入到数组末尾,这样是为了保证最近使用的组件在数组中靠后,反之靠前
keys.push(key);
} else {
// 无缓存,进行缓存
cache[key] = vnode
keys.push(key)
if (this.max && keys.length > parseInt(this.max)) {
// 超过最大缓存数量,移除第一个key对应的缓存
pruneCacheEntry(cache, keys[0], keys, this._vnode)
}
}
return vnode;
}
1️⃣ 异步组件
在项目中,有些组件不会在第一次进入首屏时加载,而是当执行了某些操作时,才会加载进来,所以此时,我们可以将该组件设置成异步加载,什么时候用,什么时候再加载进来,以达到提升首屏性能的目的。
2️⃣ 加载异步组件
点击按钮时在加载组件
<!-- >>>>>>>>>> App.vue >>>>>>>>>> -->
<template>
<div id="app">
<button @click="tf = true">点击加载异步组件</button>
<component-one v-if="tf" />
<component-two v-if="tf" />
</div>
</template>
<script>
export default {
data() {
return {
tf: false,
};
},
components: {
ComponentOne: () => import("./components/ComponentOne.vue"),
ComponentTwo: () => import("./components/ComponentTwo.vue"),
},
};
</script>
2️⃣ 合并多个异步组件
上面的例子可以看出多个异步组件会生成多个 js 文件, 就会发送几次请求引入几个 js 文件, 这相对于单个 js 来说效率是不高的, 可以通过合并异步组件来避免
将多个需要同时加载的组件合并到一个文件中:
1. 在 import 中加上注释 /* webpackChunkName: "async" */ 就可以做到将需要同时加载的文件放在同一个文件中, "async" 为文件的名称
components: {
ComponentOne: () => import(/* webpackChunkName: "async" */ "./components/ComponentOne.vue"),
ComponentTwo: () => import(/* webpackChunkName: "async" */ "./components/ComponentTwo.vue"),
},
异步加载的文件,会在 link 标签上设置 rel=”prefech”。浏览器会在空闲时间内下载对应的资源,使用时可以直接从缓存中获取。与之对应的 rel=”preload”,会及时下载对应的资源。