关键词:keep-alive、include、路由守卫、栈
一、问题背景
-
用户行为习惯不同 ==> 移动端得用路由跳转来控制页面展示
在pc端用户基本不会点击浏览器自带的返回按钮,而移动端用手机自带返回更方便。这就导致了在开发pc页面时用弹窗或者抽屉的形式展示某些内容(详情、表单等)就足够,而移动端则更多使用路由跳转。
-
路由跳转需要考虑页面缓存机制
这里参考微信小程序的页面栈的方式: 路由方式 | 页面栈表现 | | ------ | ------------------- | | 初始化 | 新页面入栈 | | 打开新页面 | 新页面入栈 | | 页面重定向 | 当前页面出栈,新页面入栈 | | 页面返回 | 页面不断出栈,直到目标返回页 | | Tab 切换 | 页面全部出栈,只留下新的 Tab 页面 | | 重加载 | 页面全部出栈,只留下新的页面 |
二、设计
在pinia里定义一个页面栈,在全局路由守卫中维护栈,在router-view中将keep-alive的include的属性值设为本栈。
include属性会根据组件的name选项进行匹配,而路由守卫只能获取到当前路由的配置。所以这里需要路由的name和组件的name手动保持一致。
在 3.2.34 或以上的版本中,使用
<script setup>的单文件组件会自动根据文件名生成对应的name选项,无需再手动声明。
3.2.34以下版本用unplugin-vue-setup-extend,更方便的设置组件name:
<!-- /src/views/login.vue -->
<script setup name="login"></script>
{
path: '/login',
name: 'login',
component: () => import('@/views/login.vue')
}
三、代码实现
// src/store/keep.js
import { defineStore } from 'pinia';
export const useKeepStore = defineStore('keep', {
state: () => ({
include: [],
}),
actions: {
add(name) {
if (!name) return;
let index = this.include.indexOf(name);
if (index + 1) {
this.include.splice(index + 1);
} else {
this.include.push(name);
}
},
},
});
// src/router/index.js
import { useUserStore } from '@/store/user';
import { useKeepStore } from '@/store/keep';
router.beforeEach((to, from, next) => {
const { token, setToken } = useUserStore();
const { add } = useKeepStore()
if (
to.name !== 'login' &&
to.matched.some((route) => route.meta.requireAuth)
) {
if (to.query.token) {
setToken(to.query.token);
}
if (token) {
add(to.name)
next();
} else {
console.log('当前尚未登录,请先登录');
add('login')
next({ name: 'login' });
}
} else {
add(to.name)
next();
}
});
// ...
<!-- src/views/App.vue -->
<template>
<router-view class="router_view" v-slot="{ Component }">
<keep-alive :include="keepStore.include">
<component :is="Component" :key="$route.path" />
</keep-alive>
</router-view>
</template>
<script setup>
import { useKeepStore } from "./store/keep";
const keepStore = useKeepStore()
</script>