vue 2.0 路由跳转时新开tab

1:封装显示tab组件

TabNav/index

复制代码
<template>
  <div class="tab-nav">
    <ul class="tabs">
      <li
        v-for="item in visitedViews"
        :key="item.path"
        :class="{ active: item.path === activeTab }"
        @click="handleTabClick(item)"
      >
        <span>{{ item.meta.title }}</span>
        <i class="el-icon-close" @click.stop="removeTab(item.path)" />
      </li>
    </ul>
  </div>
</template>

<script>
import { mapState } from 'vuex'

export default {
  data() {
    return {
      activeTab: ''
    }
  },
  computed: {
    ...mapState({
      visitedViews: state => state.tagsView.visitedViews // 显式写法更安全
    })
  },
  watch: {
    $route(newVal) {
      const exists = this.visitedViews.some(v => v.path === newVal.path)
      if (!exists) {
        this.addView(newVal)
      }
      this.activeTab = newVal.path
    }
  },
  created() {
    console.log(this.$store.state.tagsView.visitedViews, 'this.$store.state.tagsView.visitedViews')
    // this.activeTab = this.$route.path
    this.$nextTick(() => {
      const route = this.$route
      if (route.meta && route.meta.title) {
        console.log('准备添加 tab:', route)
        this.addView(this.$route)
        this.activeTab = route.path
      } else {
        console.warn('当前路由缺少 meta.title,无法添加 tab')
      }
    })
  },
  methods: {
    addView(view) {
      this.$store.dispatch('tagsView/addView', view)
    },
    removeTab(targetName) {
      const tabs = [...this.visitedViews]
      const curIndex = tabs.findIndex(v => v.path === targetName)
      if (curIndex === -1) return

      // 删除前先找到下一个 tab
      const nextTab = tabs[curIndex + 1] || tabs[curIndex - 1]
      console.log('点击删除:', targetName)
      // 分发删除 action
      this.$store.dispatch('tagsView/delView', { path: targetName }).then(({ visitedViews }) => {
        console.log('【组件】收到最新 visitedViews:', visitedViews)

        // 强制刷新缓存 key
        this.$root.$emit('update-cached-views')

        // 如果当前 tab 被删除,则跳转
        if (this.activeTab === targetName && nextTab) {
          this.activeTab = nextTab.path
          this.$router.push(nextTab.path)
        } else if (visitedViews.length === 0) {
          this.$router.push('/')
        }
      })
    },
    toLastView(visitedViews, view) {
      console.log('最新的 visitedViews:', visitedViews)
      // 先确保 visitedViews 是数组
      if (!Array.isArray(visitedViews)) {
        console.warn('visitedViews 不是数组', visitedViews)
        this.$router.push('/')
        return
      }

      const latestView = visitedViews.slice(-1)[0]
      if (latestView) {
        // 还有 tab 存在,跳转到最后一个
        this.activeTab = latestView.path
        this.$router.push(latestView.path)
      } else {
        // 没有 tab 了,跳转默认页面
        this.activeTab = '/' // 或者 '/'
        this.$router.push(this.activeTab)
      }
    },
    handleTabClick(item) {
      this.activeTab = item.path
      this.$router.push(item.path)
    }
  }
}
</script>

<style scoped>.tab-nav {
  margin-bottom: 10px;
}

.tabs {
  display: flex;
  border-bottom: 1px solid #e4e7ed;
  list-style: none;
  padding: 0;
  margin: 10px 20px;
}

.tabs li {
  border-radius: 4px 4px 0 0;
  position: relative;
  background-color: #fff;
  border: 1px solid #e4e7ed;
  border-bottom-color: transparent;
  padding: 0 10px;
  font-size: 14px;
  cursor: pointer;
  transition: all 0.3s;
  margin-right: 6px;
}

.tabs li.active {
  color: #409eff;
  font-weight: 700;
}

.tabs li:hover {
  color: #409eff;
  background-color: #f5f7fa;
  border-color: #c0c4cc;
}

.tabs li .el-icon-close {
  display: none;
  color: #909399;
  font-size: 12px;
  vertical-align: middle;
  margin-left: 5px;
}

.tabs li:hover .el-icon-close {
  display: inline-block;
}

.tabs li.active .el-icon-close {
  color: #606266;
}
</style>

2.vue x 存储

store/tagsView.js

复制代码
const state = {
  visitedViews: [],
  cachedViews: []
}

const mutations = {
  ADD_VISITED_VIEW: (state, view) => {
    if (!view.meta || !view.meta.title) return
    console.log('【mutation】正在添加 tab:', view)
    if (state.visitedViews.some(v => v.path === view.path)) return
    state.visitedViews.push({
      ...view,
      title: view.meta.title
    })
    // 如果该页面允许缓存,则加入 cachedViews
    if (view.meta.keepAlive && !state.cachedViews.includes(view.name)) {
      state.cachedViews.push(view.name)
    }
  },
  DEL_VISITED_VIEW(state, view) {
    console.log(state.cachedViews, '删除前')
    const index = state.visitedViews.findIndex(v => v.path === view.path)
    if (index > -1) {
      console.log('正在删除:', view)
      const removedView = state.visitedViews[index]
      state.visitedViews.splice(index, 1)
      // 只有被删除的 tab 才清除缓存
      const nameIndex = state.cachedViews.indexOf(removedView.name)
      if (nameIndex > -1) {
        state.cachedViews.splice(nameIndex, 1)
      }
    }
    console.log(state.cachedViews, '删除后')
  }
}

const actions = {
  addView({ commit }, view) {
    console.log('【action】准备添加 tab:', view)
    commit('ADD_VISITED_VIEW', view)
  },
  delView({ commit }, view) {
    console.log('【action】准备删除 tab:', view)
    return new Promise((resolve) => {
      commit('DEL_VISITED_VIEW', view)
      resolve({ visitedViews: [...state.visitedViews] }) // 返回拷贝后的数组
    })
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}

3:APP.vue

复制代码
<template>
  <div id="app">
    <!-- 缓存组件 -->
    <keep-alive>
      <router-view
        v-if="$route.meta.keepAlive"
        :key="$route.fullPath"
      />
    </keep-alive>
    <!-- 不缓存的组件 -->
    <router-view v-if="isRouterAlive && !$route.meta.keepAlive" :key="$route.fullPath" />
  </div>
</template>

4:路由设置

复制代码
// keepAlive:true缓存的页面   keepAlive:false缓存的页面

  {
        path: 'home',
        name: 'home',
        component: () => import('@/views/home/index'),
        meta: { title: '首页', icon: 'dashboard', keepAlive: true } 
      },
      {
        path: 'houseStaus',
        name: 'houseStaus',
        component: () => import('@/views/houseStaus/index'),
        meta: { title: '房态', icon: 'dashboard', keepAlive: false }
      },
相关推荐
爬坑的小白13 分钟前
vue x 状态管理
前端·javascript·vue.js
凌览27 分钟前
一键去水印|5 款免费小红书解析工具推荐
前端·javascript·后端
有意义27 分钟前
栈数据结构全解析:从实现原理到 LeetCode 实战
javascript·算法·编程语言
lichong95131 分钟前
鸿蒙 web组件开发
前端·typescript
1024小神32 分钟前
在html中使用js动态交换两个元素的位置
前端
鹿鹿鹿鹿isNotDefined33 分钟前
逐步手写,实现符合 Promise A+ 规范的 Promise
前端·javascript·算法
一千柯橘33 分钟前
Electron - IPC 解决主进程和渲染进程之间的通信
前端
申阳34 分钟前
Day 16:02. 基于 Tauri 2.0 开发后台管理系统-项目初始化配置
前端·后端·程序员
老前端的功夫36 分钟前
HTTP 协议演进深度解析:从 1.0 到 2.0 的性能革命
前端·网络·网络协议·http·前端框架