Vue3 提供了一些可以直接使用的内置组件,包括 Transition、TransitionGroup、KeepAlive、Teleport、Suspense。之前写了 Transition 和 TransitionGroup ,这里继续来写 KeepAlive 和 Teleport。

KeepAlive

在使用 component 元素配合 is 切换组件时,组件是会被销毁的,组件销毁时也会同时销毁相关的数据。KeepAlive 的作用就是在多个组件切换时缓存组件实例。

组件使用

下面使用 component 切换组件:

<template>
  <div>
    <button @click="tagName = 'tagPage1'">显示标签页1</button>
    <button @click="tagName = 'tagPage2'">显示标签页2</button>
    <component :is="tags[tagName]"></component>
  </div>
</template>

<script setup>
import { ref } from "vue";
import tagPage1 from './components/tagPage1.vue';
import tagPage2 from './components/tagPage2.vue';

const tags = {tagPage1, tagPage2};
const tagName = ref('tagPage1');
</script>

上面的组件中引入了 tagPage1tagPage2 组件,使用按钮可以在两个组件之间切换,效果如下:

使用component在多个组件间切换

tagPage1tagPage2 是差不多的,两个组件中都有一个 count 变量用来保存数字,点击按钮数字就会 ++。我在切换组件后,之前组件的数据就会被销毁,count 的数字也会变为 0。

下面使用 KeepAlive 来缓存上面的 tagPage1tagPage2 组件:

<KeepAlive>
  <component :is="tags[tagName]"></component>
</KeepAlive>

使用KeepAlive缓存component组件实例

我在两个组件之间切换时,组件的数据也不会丢失。

设置包含和排除组件

通过 includeexclude 属性可以设置包含和排除组件,include 是要包含的组件,exclude 是要排除的组件。

<!--include 可以传入组件对象名称,名称之间用逗号分隔-->
<KeepAlive include="tagPage1,tagPage3">
  <component :is="tags[tagName]"></component>
</KeepAlive>
<!--也可以通过正则表达式传入名称-->
<KeepAlive :include="/tagPage1|tagPage3/">
  <component :is="tags[tagName]"></component>
</KeepAlive>
<!--也可以通过数组传入名称-->
<KeepAlive :include="['tagPage1','tagPage3']">
  <component :is="tags[tagName]"></component>
</KeepAlive>

exclude 排除的写法和 include 是一样的。

设置最大缓存数

通过 max 可以设置最大缓存实例数:

<KeepAlive :max="3">
  <component :is="tags[tagName]"></component>
</KeepAlive>

如果缓存的组件超出了限制,访问次数少的组件就会被销毁。

缓存生命周期钩子

被 KeepAlive 缓存的组件,切换时只会移除页面上的 DOM 元素,移除元素后组件会变为 不活跃状态 ,切换回来又会变为 活跃状态

KeepAlive 提供了两个钩子,在切换活跃状态时,可以通过钩子触发函数。

下面给 KeepAlive 内的组件添加钩子函数:

import { onActivated, onDeactivated } from "vue";

onActivated(() => {
  alert('组件被切换为不活跃状态');
});

onDeactivated(() => {
  alert('组件被切换为活跃状态');
});

组件第一次挂载时也会触发 onActivated

Teleport

Teleport 可以把一个组件的模板内容移动到组件 DOM 之外的区域。比如你在 App 组件引入了 B 组件,B 组件里又引入了 C 组件,你可以使用 Teleport 内置组件把 C 组件里的 template 模板内容直接移动到 body 下。

Teleport 主要的使用场景就是一些需要全屏显示的模态框或大图展示之类的,使用 Teleport 把组件模板移动到外层或 body 下,可以避免父组件或父元素的 CSS 样式影响到模态框的样式。

组件使用

下面是一个 App 组件,组件里包含多个 div ,我会在最深层的 div 中插入一个 imgModal 组件:

<template>
  <div id="post-page">
    <div class="content">
      <imgModal />
    </div>
  </div>
</template>

<script setup>
import imgModal from './components/img-modal.vue';
</script>

下面是 imgModal 组件:

<template>
  <div id="img-box">
    <img src="https://www.misterma.com/img-admin/uploads/16867997522291.png" alt="大图">
  </div>
</template>

在浏览器的开发者工具中可以看到 idimg-box 的元素是在 .content 里的:

没有使用Teleport的开发者工具展示

下面给 idimg-box 的元素外添加 Teleport 组件,把 idimg-box 的元素直接移动到 body 下:

<template>
  <Teleport to="body">
    <div id="img-box">
      <img src="https://www.misterma.com/img-admin/uploads/16867997522291.png" alt="大图">
    </div>
  </Teleport>
</template>

打开开发者工具看一下:

使用Teleport把元素移动到body下

Teleportto 可以是一个 CSS 选择器字符串,也可以是一个 DOM 对象,上面的 to="body" 就是把 Teleport 内的元素直接移动到 body 下。

Teleport 只会改变 DOM 的结构,不会改变组件间的逻辑关系,A 组件下的 B 组件使用 Teleport 把模板移动到 body 下,B 组件也还是 A 组件的子组件。Props、provide 和 inject 还是按照正常的组件关系使用。

禁用 Teleport

在有的情况下可能需要根据用户的设备或设置动态的启用或禁用 Teleport,Teleport 有一个 :disabled 属性,通过 :disabled 可以禁用 Teleport :

<Teleport to="body" :disabled="true"></Teleport>

多个 Teleport 移动到同一个元素下

多个 Teleport 也可以移动到同一个目标元素下:

<Teleport to="body">
  <p>Mr. Ma's Blo www.misterma.com</p>
</Teleport>
<Teleport to="body">
  <p>Github https://github.com/changbin1997</p>
</Teleport>

移动后的顺序还是和 Teleport 的顺序一样的:

<p>Mr. Ma's Blo www.misterma.com</p>
<p>Github https://github.com/changbin1997</p>