前言
这是我的 模仿抖音 系列文章的第四篇:这篇我们将实现有条件的路由缓存,就像我们访问传统新闻网站一样
第二篇:实现抖音 "视频无限滑动"效果
最终效果
在线预览: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 友在使用!
大多数情况下,用户进入一个页面,这个页面会请求数据。用户浏览完毕退出页面。如果再次进入,页面应该像重新打开了一样,开始请求数据,这是符合用户预期的
而不是不管重新进入多少次,页面都是一开始进入的样子,这时用户可能会怀疑自己是否断网了?我们总不能在每个页面都写 onActivated
和 onDeactivated
方法去处理数据请求的逻辑吧?
解决
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
值
这样我们就实现了有条件的判断缓存,并且不需要修改任何组件的内部逻辑!
结束
以上就是文章的全部内容,感谢看到这里,希望对你有所帮助或启发!创作不易,如果觉得文章写得不错,可以点赞收藏支持一下,我会更新更多实用的前端知识与技巧,期待与你共同成长~