带你看懂Vue3 KeepAlive 及实际应用

带你看懂Vue3 KeepAlive 及实际应用

前言

在整理前端知识时,觉得vue3的keepAlive使用上与vue2略有不同,并且vue3可以通过自定义Hooks更好更方便地使用keepAlive。因此决定写一篇博客来记录和讲解下这个我们开发中一定会遇到的知识点。

keepAlive的概念

keepAlive是vue中的一个内置组件,它的功能是在多个组件间动态切换时缓存被移除的组件实例,简单理解就是,当我们路由切换到新的路由再切换回来后,原来的页面能保持原本的实例,并不会被销毁。也就是不会再次触发createmounted这些生命周期钩子函数,取而代之的是onActivatedonDeactivated两个钩子函数,具体用法将在后面讲解。

基本使用

keepAlive上的prop共有三个参数include、exclude、max,具体使用示例如下 (1)include被包涵的组件将能够被缓存

xml 复制代码
<!-- 以英文逗号分隔的字符串 -->
<KeepAlive include="a,b">
  <component :is="view" />
</KeepAlive>

<!-- 正则表达式 (需使用 `v-bind`) -->
<KeepAlive :include="/a|b/">
  <component :is="view" />
</KeepAlive>

<!-- 数组 (需使用 `v-bind`) -->
<KeepAlive :include="['a', 'b']">
  <component :is="view" />
</KeepAlive>

它会根据组件的 name 选项进行匹配,所以组件如果想要条件性地被 KeepAlive 缓存,就必须显式声明一个 name 选项
在 3.2.34 或以上的版本中,使用 <script setup> 的单文件组件会自动根据文件名生成对应的 name 选项,无需再手动声明。

(2)exclude 所包涵的组件将不会被缓存 (3)max 最多可以缓存多少个组件实例

ruby 复制代码
<KeepAlive :max="10">
  <component :is="view"></component>
</KeepAlive>

高级用法

上面我们已经介绍过了keepAlive的基本使用场景,但是在实际应用中,我们更多地是与路由组件相结合来使用,那么具体怎么用呢?

(1)首先我们在定义路由时需要在路由配置中加入配置项,判断当前路由是否需要被缓存。

css 复制代码
 {
        path: '/',
        name: 'home',
        meta: {
            keepAlive: true,
            deepth:1,
        },
        component: () => import('@/pages/home/home.vue'),
    },

(2)在app.vue中修改router-view的配置,增加keep-alive,通过上面路由中的meta值来判断当前组件是否需要缓存。

ini 复制代码
 <router-view v-slot="{ Component }">
    <keep-alive :include="includeRoute">
        <component
            :is="Component"
            :key="$route.name"
            v-if="$route.meta.keepAlive"
        />
    </keep-alive>
    <component
        :is="Component"
        :key="$route.name"
        v-if="!$route.meta.keepAlive"
    />
</router-view>

(3)通过deepth判断includeRoute中需要被缓存的页面,及时销毁无需缓存的页面。同过比对deepth,也就是页面路由的深度,这样当返回到最外层路由时,可以销毁掉深层次的无用的页面缓存。

ini 复制代码
//keepAlive路由数组
let pathDeepArr: any = [];
// 监听当前路由的name变化
watch(
    () => router.currentRoute.value,
    (newValue: any) => {
        if (newValue.meta.keepAlive) {
            let pathArr = pathDeepArr.filter((item: any) => {
                return item.name == newValue.name;
            });
            //去重,存在就不添加
            if (pathArr.length == 0) {
                pathDeepArr.push({
                    name: newValue.name,
                    deepth: newValue.meta.deepth,
                });
            }

            let tempArr = pathDeepArr.filter((item: any) => {
                return (
                    item.deepth < newValue.meta.deepth ||
                    item.deepth == newValue.meta.deepth
                );
            });
            pathDeepArr = tempArr;
            includeRoute.value =
                tempArr.length > 0
                    ? tempArr.map((item: any) => item.name)
                    : ['home'];
        }

        if (newValue.name == 'login') {
            includeRoute.value = [];
        }
    },
    { immediate: true },
);

通过上面的步骤,就能将keep-alive与路由完美的结合,那么还有个问题,比如我们在访问列表页时,页面发生了滚动,当我们点击列表中的某一条进入到详情页再返回后,会发现虽然页面被缓存了,数据也都在,但是滚动条到位置跑到了最顶端,那么我们该如何解决这个问题呢?接下来我将用一个hooks解决这个问题。

自定义Hooks

上面提到了keepalive多了两个生命周期的钩子onActivatedonDeactivated,那么我们就可以在页面滚动的时候记住滚动的距离scrollTop,当页面返回,也就是onActivated的时候,滚动到上次滚动到位置。

hooks代码如下:

ini 复制代码
import { ref, onActivated } from 'vue';
const keepAliveHook = () => {
    const scrollRef = ref<any>(null);
    const scrollTop = ref(0);
    //再次返回页面时保持原先滚动的位置
    onActivated(() => {
        scrollRef.value.scrollTop = scrollTop.value;
    });
    //页面滚动保留滚动高度
    const onScrollBody = () => {
        scrollTop.value = scrollRef.value.scrollTop || 0;
    };

    return { onScrollBody, scrollRef };
};

export default keepAliveHook;

在页面中使用:

xml 复制代码
<template>
 <div class="home-container" ref="scrollRef" @scroll="onPageScroll">
 </div>
 </template>

<script lang="ts" setup>
import keepAliveHook from '@/hooks/keepAliveHook';

const { onScrollBody, scrollRef } = keepAliveHook();
//页面滚动
const onPageScroll = () => {
    onScrollBody();
}
</script>

这样就能解决返回页面时无法自动滚动到上次位置到问题了。

最后

以上是我在vue中使用keepalive的一些应用场景,以及经验总结,欢迎掘友们提出更好更多其他用法😊

相关推荐
腾讯TNTWeb前端团队7 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰10 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪10 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪10 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy11 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom12 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom12 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom12 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom12 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom12 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试