
本文共 4571 字,大约阅读时间需要 15 分钟。
目录
系列文章
- vue3.0+ts+element-plus多页签应用模板:多级路由缓存
一、先说点啥
1. 为啥需要路由缓存?
关于这个问题,我就只假设一个场景:用户正在填写一个很长很长的表单,然后他填着填着就需要查看一下其他页面表格中的信息,这个时候他一定会去切换路由。好的,等他看完回来,他发现自己刚才用了一个小时填了一半的表单,居然被清!空!了!
当然这只是个笑话,应该没有人会设计一个这样反人类的表单,但是如果这个系统有路由缓存的功能,那么刚才的悲剧是不是就可以避免了呢?所以,路由缓存是一个很有必要的功能。
2. 怎么实现路由缓存?
vue组件缓存:keep-alive =>
路由缓存在vue中其实很容易实现,因为尤大大已经替我们写好了一个名叫keep-alive的组件,它作为一个vue的内置组件,可以在包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。这个描述是不是很符合我们想要实现的功能呢?说一下实现思路吧:
- 由于keep-alive组件在缓存多级路由的时候,表现并不好,所以我们需要将多级路由给它拍扁成二级路由,也就是路由扁平化
- 用keep-alive组件包裹住component组件(),用router-view包裹住component组件
- 每当切换路由时,向keep-alive组件的include属性中添加当前路由所对应的组件名
- 如果有无需缓存的路由,可在路由的meta中添加
noKeepAlive: true
属性,然后在切换路由时跳过当前组件
就这,是不是很简单?那么我们就去实现吧。
二、路由扁平化
首先第一步是实现路由扁平化,由于我的所有子级菜单项,都依赖于一个名叫AppMain的组件(不明白我在说什么的可以参照项目源码),也就是说,所有路由模块都没有直接引用Layout组件(你的主布局组件),而是引用了一个只有router-view的AppMain组件。所以,我们只需要将引用AppMain的中间路由删掉,就可以达到路由扁平化的目的了。所以,我们可以在你router的入口文件中这样写:
// 路由扁平化,将二级以上的路由转换成二级路由const handleKeepAlive = (to: RouteLocationNormalized) => { // 判断目标路由记录是否大于2 if (to.matched && to.matched.length > 2) { // 遍历路由记录,制作面包屑导航列表,并删除依赖于AppMain组件的中间路由,实现路由扁平化 for (let i = 0; i < to.matched.length; i++) { const element = to.matched[i] /* 从这里开始处理面包屑 */ if (to.meta.breadcrumb && element.name !== 'index') { ;(to.meta.breadcrumb as BreadcrumbItemProps[]).push({ name: element.name!, label: element.meta.label as string, type: element.meta.type as MenuItemType }) } if (!to.meta.breadcrumb) { to.meta.breadcrumb = [] } /* 从这里结束处理面包屑 */ if (element.components.default.name === 'AppMain') { to.matched.splice(i, 1) handleKeepAlive(to) } } } else { // 当路由已经扁平化完毕的时候,将目标路由自身加入面包屑中 /* 从这里开始处理面包屑 */ const toBreadcrumb = to.meta.breadcrumb as BreadcrumbItemProps[] if (!toBreadcrumb) return const isToInToBreadcrumb = toBreadcrumb.some( (item: BreadcrumbItemProps) => item.name === to.name ) if (!isToInToBreadcrumb) { ;(to.meta.breadcrumb as BreadcrumbItemProps[]).push({ name: to.name!, label: to.meta.label as string, type: to.meta.type as MenuItemType }) } /* 从这里结束处理面包屑 */ }}router.beforeEach((to, from, next) => { handleKeepAlive(to) next()})
这样,我们就做好了路由扁平化,同时顺便处理了一下面包屑导航的定位(可以先忽略,等到面包屑章节的时候再回来看)。
三、定义tag模块处理路由缓存
我们需要在每次切换路由的时候,缓存路由对应的组件名,那么我们就可以在vuex中定义一个模块,专门用来处理标签页的增删和缓存。由于我在编写各个页面的时候,就规定组件名是对应路由名的首字母大写,所以这里的组件名只需要封装一个处理方法,将路由名的首字母大写即可。在src/store/modules
下创建文件tag.ts
:
import { Module } from 'vuex'import { RouteRecord, RouteRecordName } from 'vue-router'import { TagStateProps, RootStateProps } from '../typings'const tagModule: Module= { namespaced: true, state: { cachedList: [] }, mutations: { ADD_CACHED_VIEW(state, routeName: string | RouteRecordName) { const bigName = Utils.getBigName(routeName as string) // 判断路由是否已经存在 const isViewExist = state.cachedList.some((viewName) => viewName === bigName) // 路由不存在则将路由加入cachedList if (!isViewExist) { state.cachedList.push(bigName) } }, REMOVE_CACHED_VIEW(state, routeName: string | RouteRecordName) { const bigName = Utils.getBigName(routeName as string) // 判断路由是否存在 const isViewExist = state.cachedList.some((viewName) => viewName === bigName) if (!isViewExist) { return } // 存在就删除 const viewIndex = state.cachedList.indexOf(bigName) state.cachedList.splice(viewIndex, 1) } }}export default tagModule
然后去入口文件中处理数据持久化(关于数据持久化请查看同系列文章):
// tag模块只需运行时存储const vuexSession = new VuexPersist({ storage: window.sessionStorage, key: 'vue-session', modules: ['tagModule']})const store = createStore({ modules, plugins: [vuexLocal.plugin,vuexSession.plugin]})
四、切换路由时加入缓存
现在,我们需要在切换路由的时候,将页面加入缓存。我们回到router的入口文件,修改router.beforeEach
:
router.beforeEach((to, from, next) => { if (!to.meta.noKeepAlive) { // 将路由缓存 store.commit('tagModule/ADD_CACHED_VIEW', to.name) } // 路由扁平化 handleKeepAlive(to) next()})
这里解释一下上面的tagModule/ADD_VIEW
:由于我们之前在定义模块的时候使用了namespace: true
属性,所以当我们使用tag模块的mutation或action的时候,必须要加上tagModule作为命名空间。那么为什么非要是tagModule而不是tag呢?因为我在前面处理模块名的时候,规定了模块名必须是模块文件名加上Module,所以tag模块的模块名就是tagModule。
五、使用keep-alive
前面我们已经定义好了tagModule中的cacheList作为已经缓存的路由列表,现在我们只需将这个数组添加到keep-alive的include属性中就可以实现路由缓存了。
我们找到layout中的router-view,然后改成下面这样:
const cachedList = computed(() => store.state.tagModule.cachedList)return { cachedList }
至此,我们的多级路由缓存就做好了。
下一篇预告:vue3.0+ts+element-plus多页签应用模板:头部工具栏(上)
发表评论
最新留言
关于作者
