vue实现Tab切换功能

article/2025/9/1 6:24:35

在项目开发中,我们经常会碰到Tab切换的功能,而在Vue中想实现这样的功能也应该有很多种,常用的三种应该是 Tab路由切换Tab动态组件切换通过v-show设置Tab显示隐藏。每种方法实现起来其实都不难,看看官网介绍或看几篇博客应该就能实现。

但这里面其实还有很多细节需要我们去做,如

  1. Tab切换时,切换过的Tab组件状态怎样缓存
  2. 在项目中经常会有 页面A -> 页面B -> 页面C 则从 页面C 返回 页面B 时 页面B 使用缓存数据,而页面A 跳到 页面B 时,则页面B每次都请求最新数据。比如我们在某APP内点击 最新新闻(页面A) 选项 跳转到 新闻列表(页面B) 选择某一条新闻 跳转到 新闻详情(页面C) 页面,我们希望,从新闻详情返回到新闻列表时,直接用刚才请求的数据,而不每次都重新发送请求,而从 最新新闻 跳转到 新闻列表时,则都请求最新的数据
  3. 父组件如何给子组件传递参数
  4. 页面内Tab来回切换后,如何直接返回到上一级页面
  5. 页面循环切换时,前进或后退如何保证页面结构正确(具体下面会讲到)

Tab路由切换带缓存

想要通过路由进行切换,就需要使用嵌套路由,即整个大页面是一个路由,点击不同Tab时,再通过嵌套路由来切换不同的路由。
想要Tab切换时保存当前状态可以使用keep-alive包裹,keep-alive具体使用参考这篇文章-vue中动态添加和删除组件缓存 keep-alive
包裹Tab的组件页面我们也要动态的缓存,这里也需要用到keep-alive,只是这个keep-alive需要添加到App.vue内,各个组件的动态缓存我们使用的是keep-alive的include属性。缓存最大数使用max属性

router-link介绍

  • 通过to属性链接目标路由,当被点击时,内部会立刻把 to 的值传到 router.push(),既然是通过router.push()的方式跳转,那么就会往history记录中添加,这样当返回时,可能就会先从Tab3返回到Tab2再返回到Tab1再返回,这种体验很不好,怎样一步返回呢,就是在router-link中添加replace属性,这样当点击时,会调用 router.replace() 而不是 router.push(),于是导航后不会留下 history 记录,这样就可一步返回了,如:<router-link :to="{ path: '/abc'}" replace></router-link>
  • 通过 命名的路由 传递参数,如:<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
  • 通过 带查询参数 传递参数,如:<router-link :to="{ path: 'register', query: { plan: 'private' }}">Register</router-link>,结果路由为:/register?plan=private
  • router-link设置点击事件时需要添加 natvie, 如@click.native="TabClick()"

思路

  1. 通过router配置嵌套路由
  2. 通过使用keep-alive的include属性有条件的缓存组件
  3. 通过store响应式的修改include属性对应的值
  4. 通过组件内导航钩子beforeRouteEnter、beforeRouteLeave给store提交mutations修改

实例演示

1:page1->news->page2 然后再依次返回在这里插入图片描述

通过演示我们发现

  1. 从page2返回到news时,总是能返回到我们之前保存的状态
  2. 从news返回到page1后,再从page1跳转news,不管news之前是什么状态,都会初始化显示购物的页面

2:page1(1)->news(2)->page2(3)->page1(4)->news(5)->page2(6) 然后再依次返回
在这里插入图片描述
这个视频里有几个问题需要我们去思考

  1. 第四步跳转到第五步,为什么Tab选中为购物、内容选中为鞋包,为什么news组件及内部路由组件都缓存着
  2. 第三步返回到第二步,为什么Tab选中为购物、内容选中为母婴,但从右边缓存的组件看,为什么shopping组件也被缓存了

这两个问题我们后边会具体介绍

部分代码示例

1:在router中配置各个路由

这里需要注意,配置children子路由时path不能加 / ,在router-link的to后面写的路由需要以 / 开头,以 / 开头的嵌套路径会被当作根路径

export default new Router({routes: [{path: '/page1',name: 'page1',component: () => import(/* webpackChunkName: "test" */ './views/news/page1.vue')}, {path: '/page2',name: 'page2',component: () => import(/* webpackChunkName: "test" */ './views/news/page2.vue')}, {path: '/news',name: 'newsIndex',component: () => import(/* webpackChunkName: "test" */ './views/news/news.vue'),children: [{path: 'sports',name: 'sports',component: () => import(/* webpackChunkName: "test" */ './views/news/sports.vue'),}, {path: 'shopping',name: 'shopping',component: () => import(/* webpackChunkName: "test" */ './views/news/shopping.vue'),}, {path: 'learn',name: 'learn',component: () => import(/* webpackChunkName: "test" */ './views/news/learn.vue'),}]}]
})
2:在App.vue组件通过computed计算属性响应式的获取store里的keepAliveArr计算属性,并赋值给keep-alive的include属性,并设置最多可缓存5个组件
  <template><div id="app"><keep-alive :include="keepAliveArr" :max="5"><router-view></router-view></keep-alive></div></template><script>export default {computed: {keepAliveArr() {return this.$store.getters.keepAliveArr}}}
3:在store的mutations中提供状态更改的方法,并通过store的计算属性供外部访问
  import Vue from 'vue'import Vuex from 'vuex'Vue.use(Vuex);export default new Vuex.Store({state: {//缓存组件数组keepAliveArr: []},mutations: {UPDATE_KEEP_ALIVE(state, payload) {//当payload.type不为空则代表清除指定缓存组件,否则添加指定组件if (payload.type) {let index = state.keepAliveArr.indexOf(payload.keepAlive);if (index !== -1) {state.keepAliveArr.splice(index, 1); //删除数组的缓存的组件}} else {let index = state.keepAliveArr.indexOf(payload.keepAlive);if (index === -1) {state.keepAliveArr.push(payload.keepAlive); //添加需要缓存的组件}}}},getters: {keepAliveArr: state => state.keepAliveArr}})
4:在组件内通过导航钩子beforeRouteEnter、beforeRouteLeave给store提交mutations修改缓存组件keepAliveArr的值

这里 page1为news的上一个页面,page2为下一个页面,通过beforeRouteEnter钩子,不管从哪个页面进入都提交mutations,缓存当前news页面,当离开时判断,如果是返回上一个页面则删除当前news页面缓存,当删除news页缓存时,内部通过keep-alive保存的 购物、体育、学习三个组件缓存的状态也会一并删除,即内部的在激活的和被停用的组件都会执行销毁的生命周期

注意:如果我们的页面比较简单,最深跳转到page2,即: page1->news->page2,然后再一级一级返回的话,那么beforeRouteEnter这两个if判断可以不写

4.1:beforeRouteEnter中两个if判断解释

4.1.1:第一个if判断

当循环跳转时,即

 page1(1)->news(2)->page2(3)->page1(4)->news(5)

因为page1跳转的路径 永远是 /news/shopping,news组件又通过keep-alive保存当前状态,所以在第二步news内如果 点击了 Tab体育 或者 Tab学习时,此时currentTab不为0,但当通过 第三步->第四步->第五步 再次跳转到news 时,由于page1路径 永远是 /news/shopping,而news状态还保存在内存里不会重新 创建,此时Tab指示器显示的和下面具体内容就会不一致,所以这里判断如果是这种情况就 强制切换 到 /news/shopping 页

4.1.2: 第二个if判断

当循环跳转时又依次返回时。即

 page1(1)->news(2 Tab选择 学习)->page2(3)->page1(4)->news(5 Tab选择 体育)->page2(6)

现在开始返回, 返回到第五步Tab体育时,是没有问题的,因为 news状态是缓存 的, 而第五步Tab体育页返回第四步page1时,这里 beforeRouteLeave中我们已经把news页设置不缓存 了,再继续返回到第三步page2,再返到第二步Tab学习页时,因为 最初 我们是从第二步Tab学习的路由往 下一页page2页跳的,所以这里返回也是返回到Tab学习的路由页即 /news/learn ,但因为整个news已经不缓存了,所以这里返回从第三步返回到第二步时,其实 news所有的生命周期都会执行 ,此时 currentTab的值为0,如果不通过这个判断,那么Tab指示器显示的和下面具体内容也会不一致,所以这种情况我也 强制让切换 到 /news/shopping 页

4.2:组件内导航守卫的to.path 和 to.fullPath 区别?
  • to.path: 是我们在router路由里定义的路由,如/news/shopping
  • to.fullPath: 是包括我们跳转路由时传递的参数,如/news/shopping?content=购物
   beforeRouteEnter(to, from, next) {next(vm => { //添加组件缓存vm.$store.commit("UPDATE_KEEP_ALIVE", {keepAlive: 'news'});let path = '';//当循环跳转时,替换路由为shopping页if (vm.currentTab !== 0 && from.path === '/page1') {vm.currentTab = 0;path = '/news/shopping';vm.$router.replace({path,query: {content: '购物'}});}//当循环跳转后,循环返回时,替换路由为shopping页if (vm.currentTab === 0 && to.path !== '/news/shopping') {vm.currentTab = 0;path = '/news/shopping';vm.$router.replace({path,query: {content: '购物'}});}})},beforeRouteLeave(to, from, next) {if (to.path === '/page1') {//删除缓存this.$store.commit("UPDATE_KEEP_ALIVE", {type: 1,keepAlive: 'goods'})}next()},

总结

通过上面四步就可以实现Tab路由切换并带组件状态缓存,这个keep-alive嵌套keep-alive需要注意的事项,大家可以参考这篇文章-vue中动态添加和删除组件缓存 keep-alive

Tab动态组件切换

大家可以参考这篇文章-vue中动态添加和删除组件缓存 keep-alive

通过v-show设置Tab显示隐藏

这个就不写了,大家只要慢慢写应该都能实现,只是用这种方式实现不太优雅。

Tab路由切换的完成代码

news代码

  <template><div class="list-container"><div class="btn" @click="btnJumpClick">跳转到page2详情页</div><nav class="tab-root"><!--通过query向子路由传递参数--><router-link v-for="(item,index) in routerList":key="index"class="tab-button":to="{path:item.url,query:{content:item.content}}"replace:class="{ active: currentTab === index }"@click.native="currentTab = index">{{item.tab}}</router-link></nav><keep-alive :include="cached" :max="3"><router-view class="view"></router-view></keep-alive></div></template><script>export default {name: "news",data() {return {currentTab: 0,cached: 'shopping,sports,learn',about: '/news/shopping',routerList: [{tab: '购物',url: '/news/shopping',content: '购物'}, {tab: '运动',url: '/news/sports',content: '运动'}, {tab: '学习',url: '/news/learn',content: '学习'}]}},activated() {console.log("--news--activated--");},deactivated() {console.log("--news--deactivated--");},beforeRouteEnter(to, from, next) {next(vm => { //添加组件缓存vm.$store.commit("UPDATE_KEEP_ALIVE", {keepAlive: 'news'});let path = '';//当循环跳转时,替换路由为shopping页if (vm.currentTab !== 0 && from.path === '/page1') {vm.currentTab = 0;path = '/news/shopping';vm.$router.replace({path,query: {content: '购物'}});}//当循环跳转后,循环返回时,替换路由为shopping页if (vm.currentTab === 0 && to.path !== '/news/shopping') {vm.currentTab = 0;path = '/news/shopping';vm.$router.replace({path,query: {content: '购物'}});}})},beforeRouteLeave(to, from, next) {if (to.path === '/page1') {//删除缓存this.$store.commit("UPDATE_KEEP_ALIVE", {type: 1,keepAlive: 'news'})}next()},methods: {btnJumpClick() {this.$router.push({path: '/page2'})},}}</script><style scoped lang="scss">.list-container {.btn {width: 100%;height: 40px;background: #f00;font-size: 20px;color: white;}.tab-root {display: flex;border-bottom: 1px solid #eee;}.tab-button {background: #fff;line-height: 40px;height: 40px;text-align: center;flex: 1;font-size: 15px;font-weight: normal;}.tab-button.active {font-size: 17px;font-weight: 500;border-bottom: 2px solid #f00;}}

shopping代码

  <template><div class="recommends-tab"><ul class="recommends-sidebar"><li v-for="recommend in recommends":key="recommend.id":class="{ selected: recommend === selectedRecommend }"@click="selectedRecommend = recommend">{{ recommend.title }}</li></ul><div class="selected-recommend-container"><div class="selected-recommend"><div v-html="selectedRecommend.content"></div></div></div></div></template><script>export default {name: "shopping",props:{componentTabName:String},data() {return {recommends: [{id: 1,title: '母婴',content: '<p>儿童玩具、尿裤湿巾、奶粉辅食</p>'},{id: 2,title: '鞋包',content: '<p>功能箱包、人气热卖、服饰配件</p>'},{id: 3,title: '水果',content: '<p>瓜果桃李、海鲜水产、熟食凉菜</p>'}],selectedRecommend: {}}},beforeMount() {//获取通过路由传递过来的参数console.log(this.$route.query.content);},}</script><style scoped lang="scss">.recommends-tab {display: flex;}.recommends-sidebar {width: 20%;text-align: center;background: #eee;height: 100vh;}.recommends-sidebar li {height: 30px;line-height: 30px;}.recommends-sidebar li.selected {background: #fff;color: red;}.selected-recommend-container {padding-left: 10px;}</style>

page1跳转代码

  btnLuYouClick() {this.$router.push({path: '/news/shopping',query: {content: '购物'}});}

page2跳转代码

  btnJumpClick() {this.$router.push({path: '/page1'})},

sports和learn代码比较简单就不粘贴了

上面的代码应该已经够用,如果需要全部详细代码的就留言吧,我再单独发你。


http://chatgpt.dhexx.cn/article/uGQ8MfCW.shtml

相关文章

tab切换效果

1.效果图 2.分析步骤 1.首先写vue先引入&#xff1a;<script src"https://cdn.jsdelivr.net/npm/vue2.6.14/dist/vue.js"></script> 2.接着写静态布局 3.挂载dom 4.添加指令 5.肯定要储存数据 6. 最后效果实现 3.代码块部分 按步骤操作 1.首先&#xf…

vue中如何实现tab切换功能?

一、v-show控制内容切换 1&#xff09;简单版原理&#xff1a;用点击事件改变num值作为开关&#xff0c;控制tab样式和内容显示隐藏。 2&#xff09;数据渲染原理&#xff1a;主要利用v-for绑定的index来控制&#xff0c;跟上面差不多。 二、组件切换。 知识点主要是vue中is的…

点击tabs切换不同的内容

1.通过v-for遍历posts,然后渲染数据 2.定义currentTabs变量 3.运用computed计算属性 4.点击按钮时&#xff0c;切换下边的内容 5.点击切换tabs时&#xff0c;高亮当前tabs 4.将切换tabs的文件封装成组件&#xff0c;可以使用keep-alive进行缓存数据 5.使用keep-alive触发的生命…

Tab选项卡切换

Tab选项卡切换 基本代码 HTML代码&#xff1a; <div id"notice" class"notice"><!-- 标题--><div id"notice-tit" class"notice-tit"><ul><li><a href"#">公告</a></li>…

【JS实现tab切换】

JavaScript实现tab切换。 点击科技显示图一, 点击探索显示图二。 body部分&#xff1a; <div class"box"><ul><li class"active">科技</li><li>探索</li></ul><ol><li class"active">科…

Tab页面切换

页面效果如图 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta http-equiv"X-UA-Compatible" content"IEedge"> <meta name"viewport" content"widthdevice-…

Vue实现Tab切换效果

通过Vue实现简单的Tab切换 实现思路是点击上方的标题&#xff0c;下方的内容随之发生改变&#xff0c;上方和下方用的是两个块&#xff0c;是兄弟节点&#xff0c;所以需要点击tab标题和下方内容一一对应&#xff0c;基予两个模块若下标相同是一个内容实现的。Tab切换第一步先…

html的tab切换

离开学还有10天了&#x1f630; 今天再水一篇博客 &#xff08;如图&#xff09; 通过点击来切换tab。 具体思路十分简单&#xff0c;将这些都先包含进一个大的div 先是html部分 <div class"body1"><div class"game"><ul><li>…

Vue实现选项卡切换,tab切换

1、实现tab切换就是 单击一个选项卡显示其对应的内容&#xff0c;且被点击的选项卡改变颜色&#xff0c;下面有两种实现方法&#xff08;都不要忘了vue.js的目录要写正确&#xff09; 2、第一种方法效果图 这个没什么可说的&#xff0c;直接看代码吧&#xff08;两种方式&…

最简单的Tab切换

HTML <div class"main"><div class"btn"><!-- Tab标题 --><span class"active spanList">课程介绍</span><span class"spanList">用户故事</span><span class"spanList">…

简单的tab选项卡切换

Tab选项卡切换 我们在网页上经常能看到的一个现象点击某一文字就会跳转出宁一个页面、而在同一个页面中点击头部内容&#xff0c;主体内容也会随之改变。前者是a 标签所实现的效果&#xff0c;而后者则是tab选项卡的切换。接下来我们一起来看看tab选项卡怎么个切换方法&#x…

前端——tab 栏切换案例

案例 当鼠标点击上面相应的选项卡&#xff08;tab&#xff09;&#xff0c;下面内容跟随变化 分析 Tab栏切换有2个大的模块上面的模块选项卡&#xff0c;点击某一个&#xff0c;当前这一个底色会是红色&#xff0c;其余不变&#xff08;排他思想&#xff09; 修改类名的方式…

tab栏切换

tab栏切换 当鼠标点击上面相应的选项卡(tab)&#xff0c;下面内容也跟随变化。 【案例分析】 1&#xff1a;Tab栏切换与两个大的模块 2&#xff1a;上面的模块选项卡&#xff0c;点击某一个&#xff0c;当前这一个底色会变成红色&#xff0c;其他不变(排它思想) &#xff0c;…

不定积分基本公式

不定积分基本公式

数学公式大全--极限、微分、积分

目录 1 伽马函数 1 2 常用极限公式 1 3 常用导数/微分公式 2 4 常用积分公式 2 4.1 含指数函数的积分 3 4.2 含有对数的积分 4 4.3 含有三角函数的积分 5 4.4 含根号的积分 8 4.5 含有x2a2的积分 9 4.6 含有ax2b的积分 9 4.7 含有ax2bc的积分 10 4.8 含有根号x2a2的积分 11 4.…

【数学-常用公式】基本积分表

【基本积分表】 【基本积分表的扩充】 注&#xff1a;来自《高等数学》&#xff08;第五版&#xff09;上册 同济大学数学系

【数学】定积分计算

目录 1.定义2.定理与性质2.1积分上限函数2.2积分中值定理2.3其它性质与定理 3.做题技巧、题型 1.定义 i f lim ⁡ x → λ ∑ i 1 n f ( ξ i ) △ x i ∃ &#xff0c; 则 f ( x ) 在 [ a , b ] 上 可 积 if \lim_{x \to \lambda} \sum_{i1}^n f(\xi_i)△x_i \exists&#x…