1️⃣ 动态组件

有的时候,在不同组件之间进行动态切换是非常有用的,比如在一个多标签的界面里:
image.png
image.png

1️⃣ 动态组件的使用示例

上述内容可以通过 Vue 的 元素加一个特殊的 is attribute 来实现:
image.png
016.gif

1️⃣ 动态组件缓存

动画.gif
你会注意到,如果你选择了一篇文章,切换到 Archive 标签,然后再切换回 Posts,是不会继续展示你之前选择的文章的。这是因为你每次切换新标签的时候,Vue 都创建了一个新的 currentTabComponent 实例。
重新创建动态组件的行为通常是非常有用的,但是在这个案例中,我们更希望那些标签的组件实例能够被在它们第一次被创建的时候缓存下来。为了解决这个问题,我们可以用一个 元素将其动态组件包裹起来。

注意这个 要求被切换到的组件都有自己的名字,不论是通过组件的 name 选项还是局部/全局注册。

image.png
017.gif

2️⃣ keep-alive

keep-alive 组件是 vue 的内置组件,用于缓存内部组件实例。这样做的目的在于,keep-alive 内部的组件切回时,不用重新创建组件实例,而直接使用缓存中的实例,一方面能够避免创建组件带来的开销,另一方面可以保留组件的状态。
image.png

2️⃣ include & exclude & max

keep-alive 具有 include 和 exclude 属性,通过它们可以控制哪些组件进入缓存。另外它还提供了 max 属性,通过它可以设置最大缓存数,当缓存的实例超过该数时,vue 会移除最久没有使用的组件缓存。
image.png
image.png

2️⃣ activated & deactivated

受 keep-alive 的影响,其内部所有嵌套的组件都具有两个生命周期钩子函数,分别是 activateddeactivated,它们分别在组件激活和失活时触发。第一次 activated 触发是在 mounted 之后

  1. 1. **activated:**keep-alive 组件激活时调用。
  2. 2. **deactivated:**keep-alive 组件停用时调用。

image.png
018.gif
在具体的实现上,keep-alive 在内部维护了一个 key 数组和一个缓存对象

  1. // keep-alive 内部的声明周期函数
  2. created () {
  3. this.cache = Object.create(null)
  4. this.keys = []
  5. }

key 数组记录目前缓存的组件 key 值,如果组件没有指定 key 值,则会为其自动生成一个唯一的 key 值
cache 对象以 key 值为键,vnode 为值,用于缓存组件对应的虚拟 DOM
在 keep-alive 的渲染函数中,其基本逻辑是判断当前渲染的 vnode 是否有对应的缓存,如果有,从缓存中读取到对应的组件实例;如果没有则将其缓存。
当缓存数量超过 max 数值时,keep-alive 会移除掉 key 数组的第一个元素

  1. render(){
  2. const slot = this.$slots.default; // 获取默认插槽
  3. const vnode = getFirstComponentChild(slot); // 得到插槽中的第一个组件的vnode
  4. const name = getComponentName(vnode.componentOptions); //获取组件名字
  5. const { cache, keys } = this; // 获取当前的缓存对象和key数组
  6. const key = ...; // 获取组件的key值,若没有,会按照规则自动生成
  7. if (cache[key]) {
  8. // 有缓存
  9. // 重用组件实例
  10. vnode.componentInstance = cache[key].componentInstance
  11. remove(keys, key); // 删除key
  12. // 将key加入到数组末尾,这样是为了保证最近使用的组件在数组中靠后,反之靠前
  13. keys.push(key);
  14. } else {
  15. // 无缓存,进行缓存
  16. cache[key] = vnode
  17. keys.push(key)
  18. if (this.max && keys.length > parseInt(this.max)) {
  19. // 超过最大缓存数量,移除第一个key对应的缓存
  20. pruneCacheEntry(cache, keys[0], keys, this._vnode)
  21. }
  22. }
  23. return vnode;
  24. }

1️⃣ 异步组件

在项目中,有些组件不会在第一次进入首屏时加载,而是当执行了某些操作时,才会加载进来,所以此时,我们可以将该组件设置成异步加载,什么时候用,什么时候再加载进来,以达到提升首屏性能的目的。

2️⃣ 加载异步组件

点击按钮时在加载组件

  1. <!-- >>>>>>>>>> App.vue >>>>>>>>>> -->
  2. <template>
  3. <div id="app">
  4. <button @click="tf = true">点击加载异步组件</button>
  5. <component-one v-if="tf" />
  6. <component-two v-if="tf" />
  7. </div>
  8. </template>
  9. <script>
  10. export default {
  11. data() {
  12. return {
  13. tf: false,
  14. };
  15. },
  16. components: {
  17. ComponentOne: () => import("./components/ComponentOne.vue"),
  18. ComponentTwo: () => import("./components/ComponentTwo.vue"),
  19. },
  20. };
  21. </script>

image.png动画.gif

2️⃣ 合并多个异步组件

上面的例子可以看出多个异步组件会生成多个 js 文件, 就会发送几次请求引入几个 js 文件, 这相对于单个 js 来说效率是不高的, 可以通过合并异步组件来避免
将多个需要同时加载的组件合并到一个文件中:

  1. 1. import 中加上注释 /* webpackChunkName: "async" */ 就可以做到将需要同时加载的文件放在同一个文件中, "async" 为文件的名称
  1. components: {
  2. ComponentOne: () => import(/* webpackChunkName: "async" */ "./components/ComponentOne.vue"),
  3. ComponentTwo: () => import(/* webpackChunkName: "async" */ "./components/ComponentTwo.vue"),
  4. },

image.png
异步加载的文件,会在 link 标签上设置 rel=”prefech”。浏览器会在空闲时间内下载对应的资源,使用时可以直接从缓存中获取。与之对应的 rel=”preload”,会及时下载对应的资源。
image.png