vue3.0+ts+element-plus多页签应用模板:多级路由缓存
发布日期:2021-05-07 18:29:44 浏览次数:23 分类:精选文章

本文共 4571 字,大约阅读时间需要 15 分钟。

目录

系列文章

  • vue3.0+ts+element-plus多页签应用模板:多级路由缓存

一、先说点啥

1. 为啥需要路由缓存?

关于这个问题,我就只假设一个场景:用户正在填写一个很长很长的表单,然后他填着填着就需要查看一下其他页面表格中的信息,这个时候他一定会去切换路由。好的,等他看完回来,他发现自己刚才用了一个小时填了一半的表单,居然被清!空!了!

当然这只是个笑话,应该没有人会设计一个这样反人类的表单,但是如果这个系统有路由缓存的功能,那么刚才的悲剧是不是就可以避免了呢?所以,路由缓存是一个很有必要的功能。

2. 怎么实现路由缓存?

vue组件缓存:keep-alive =>

路由缓存在vue中其实很容易实现,因为尤大大已经替我们写好了一个名叫keep-alive的组件,它作为一个vue的内置组件,可以在包裹动态组件时,会缓存不活动的组件实例,而不是销毁它们。这个描述是不是很符合我们想要实现的功能呢?说一下实现思路吧:

  1. 由于keep-alive组件在缓存多级路由的时候,表现并不好,所以我们需要将多级路由给它拍扁成二级路由,也就是路由扁平化
  2. 用keep-alive组件包裹住component组件(),用router-view包裹住component组件
  3. 每当切换路由时,向keep-alive组件的include属性中添加当前路由所对应的组件名
  4. 如果有无需缓存的路由,可在路由的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多页签应用模板:头部工具栏(上)

上一篇:盒子居中的几种常用方法
下一篇:vue3.0+ts+element-plus多页签应用模板:如何优雅地使用Svg图标

发表评论

最新留言

网站不错 人气很旺了 加油
[***.192.178.218]2025年04月05日 15时33分07秒