Vue 有条件路由缓存,就像传统新闻网站一样

前言

这是我的 模仿抖音 系列文章的第四篇:这篇我们将实现有条件的路由缓存,就像我们访问传统新闻网站一样

第一篇:200行代码实现类似Swiper.js的轮播组件

第二篇:实现抖音 "视频无限滑动"效果

第三篇:Vue 路由使用介绍以及添加转场动画

最终效果

在线预览:dy.ttentau.top/

因为这个项目是为移动端做的,推荐大家切换到手机模式访问。先按F12调出控制台,再按Ctrl+Shift+M切换到手机模式

Github地址:github.com/zyronon/dou...

实现

原理

KeepAlive 组件介绍:cn.vuejs.org/guide/built...

Vue 内置的 <KeepAlive> 组件可以缓存任何组件实例,只需要像下面这样写就可以缓存所有组件了

js 复制代码
<router-view v-slot="{ Component }">
  <keep-alive>
    <component :is="Component" />
  </keep-alive>
</router-view>

问题

可是,这样写的话,那么所有的组件都被无差别缓存了,这显然不是我们希望看到的效果。

我们期望的效果是什么?就像 jQuery 时代的网站(例如:www.v2ex.com )一样,点击一个链接,前进到下一页,浏览完成之后,返回上一页,当前页面被丢弃。再次访问的话需要重新加载

吐槽:找了一圈国内的知名网站,点击链接全是单独打开一个标签页,根本没有在当前页跳转的了!

有喜欢逛 v2ex 的朋友可以试试我开发的这个油猴脚本:V2Next ,有UI美化、楼中楼、回复上下文、高赞回复、简洁模式、发送图片和表情 emoji、base64 解码等功能,现在每天都有约1800个 V 友在使用!

大多数情况下,用户进入一个页面,这个页面会请求数据。用户浏览完毕退出页面。如果再次进入,页面应该像重新打开了一样,开始请求数据,这是符合用户预期的

而不是不管重新进入多少次,页面都是一开始进入的样子,这时用户可能会怀疑自己是否断网了?我们总不能在每个页面都写 onActivatedonDeactivated 方法去处理数据请求的逻辑吧?

解决

KeepAlive 的 exclude 属性

exclude 属性可以排除我们不需要缓存的组件,它会根据组件的 name(不是路由的 name 哦) 选项进行匹配,所以组件如果想要条件性地被 KeepAlive 缓存,就必须显式声明一个 name 选项。

在 3.2.34 或以上的版本中,使用 <script setup> 的单文件组件会自动根据文件名生成对应的 name 选项,即使是在配合 <KeepAlive> 使用时也无需再手动声明。

在 3.2.34 以下的版本中,给 <script setup> 的单文件组件设置名字需要单独安装 unplugin-vue-macros 插件,才可以使用 defineOptions 这个宏,这个宏可以用来直接在 <script setup> 中声明组件名称

js 复制代码
defineOptions({
  name: 'XXX'
})

区分是"前进"还是"后退"

我上一篇文章:Vue 路由使用介绍以及添加转场动画 里面有讲到怎样区分是"前进"还是"后退"

条件判断

pinia 中用一个数组 excludeNames 存放要排除的组件 name,并添加一个用于更新的 updateExcludeRoutes 方法

js 复制代码
<router-view v-slot="{ Component }">
  <keep-alive :exclude="store.excludeNames">
    <component :is="Component" />
  </keep-alive>
</router-view>
js 复制代码
import { defineStore } from 'pinia'

export const useBaseStore = defineStore('base', {
  state: () => {
    return {
       excludeNames: [],
    }
  },
  actions: {
    updateExcludeRoutes(val) {
      if (val.type === 'add') {
        if (!this.excludeNames.find((v) => v === val.value)) {
          this.excludeNames.push(val.value)
        }
      } else {
        const resIndex = this.excludeNames.findIndex((v) => v === val.value)
        if (resIndex !== -1) {
          this.excludeNames.splice(resIndex, 1)
        }
      }
    }
  }
})

原理和路由转场动画的原理差不多

当我们 前进 时,从 excludeNames 数组中 删除 目的页面组件的 name 值,那么目的页面将被缓存

当我们 后退 时,从 excludeNames 数组中 添加 当前页面组件的 name 值,那么当前页面被不会被缓存

这里有点绕口,一定要区分清楚,应该删除哪个页面的 name 和应该添加哪个页面的 name,我用文字也没办法讲的太明白,大家结合代码理解下

Vue-Router 的路由守卫里面编写如下代码

js 复制代码
router.beforeEach((to, from) => {
  const toDepth = routes.findIndex((v) => v.path === to.path)
  const fromDepth = routes.findIndex((v) => v.path === from.path)
  if (toDepth > fromDepth) {
    if (to.matched && to.matched.length) {
      const toComponentName = to.matched[0].components?.default.name
      baseStore.updateExcludeNames({ type: 'remove', value: toComponentName })
      // console.log('前进,删除', toComponentName)
    }
  } else {
    if (from.matched && from.matched.length) {
      const fromComponentName = from.matched[0].components?.default.name
      baseStore.updateExcludeNames({ type: 'add', value: fromComponentName })
      // console.log('后退,添加', fromComponentName)
    }
  }
  return true
})

总结

每次路由变更,路由守卫都会判断是前进还是后退,然后再对 excludeNames 数组进行删除或者添加 组件的 name

这样我们就实现了有条件的判断缓存,并且不需要修改任何组件的内部逻辑!

结束

以上就是文章的全部内容,感谢看到这里,希望对你有所帮助或启发!创作不易,如果觉得文章写得不错,可以点赞收藏支持一下,我会更新更多实用的前端知识与技巧,期待与你共同成长~

相关推荐
可爱的秋秋啊3 小时前
vue3,element ui框架中为el-table表格实现自动滚动,并实现表头汇总数据
前端·vue.js·笔记·elementui
HED5 小时前
VUE项目发版后用户访问的仍然是旧页面?原因和解决方案都在这啦!
前端·vue.js
FIT2CLOUD飞致云6 小时前
问答页面支持拖拽和复制粘贴文件,MaxKB企业级AI助手v1.10.6 LTS版本发布
人工智能·开源
清风细雨_林木木6 小时前
Vue开发网站会有“#”原因是前端路由使用了 Hash 模式
前端·vue.js·哈希算法
爱喝奶茶的企鹅7 小时前
Ethan独立开发产品日报 | 2025-04-24
人工智能·程序员·开源
局外人LZ7 小时前
前端项目搭建集锦:vite、vue、react、antd、vant、ts、sass、eslint、prettier、浏览器扩展,开箱即用,附带项目搭建教程
前端·vue.js·react.js
宝拉不想努力了7 小时前
vue element使用el-table时,切换tab,table表格列项发生错位问题
前端·vue.js·elementui
少年、潜行8 小时前
【开源】STM32HAL库移植Arduino OneWire库驱动DS18B20和MAX31850
stm32·嵌入式硬件·开源·ds18b20·max31850
神仙别闹9 小时前
基于VUE+Node.JS实现(Web)学生组队网站
前端·vue.js·node.js
HuaHua的世界11 小时前
说说 Vue 中 CSS scoped 的原理?
css·vue.js