这里举个例子:就是类似于面包屑,会记录所有的打开过的页面,方便定位到某个页面。(因为这个例子核心就是路由监听)
思路:
首先首页会一直显示,不可以移除。 我这里把其他的路由对象存在Vuex中(mainTabs:[]),需要监听路由的变化,当路由改变就往里面push(注意这里需要判断一下,只有当前路由没在mainTabs里,才会往里面push),因为存的都是路由对象,点击就跳转到对应的页面就可以。然后就是,当我们移除的时候直接splice删除掉就可以(这里注意如果删除的是当前正打开的页面,需考虑页面跳转的问题)
先说Vue2的实现
1.先在Vuex定义好一个空数组
export default {
namespaced: true,
state: {
// 面包屑
mainTabs:[]
},
mutations: {
updatemainTabs(state,val) {
if(val.constructor === Object) {
if(state.mainTabs.every(item => item.path != val.path)) {
state.mainTabs.push(val)
}
}else {
state.mainTabs.splice(val,1)
}
}
}
}
2. 我这里将面包屑单独抽离出来单独的组件,只写面包屑的逻辑
<!-- 面包屑组件 -->
<template>
<div style="width:100%;height: 100%;overflow-x: auto;overflow-y: hidden;">
<el-tabs v-model="currentNamePath" type="card" @tab-click="tabClick" @tab-remove="tabRemove">
<el-tab-pane :label="'首页'" :name="'/home'"></el-tab-pane>
<el-tab-pane v-for="(mainTab,index) in mainTabs" :key="mainTab.path" :label="mainTab.meta.title" :name="mainTab.path" closable></el-tab-pane>
</el-tabs>
</div>
</template>
<script>
export default {
data() {
return {
breadcrumbList:[],
currentNamePath:'/home'
}
},
mounted() {
},
computed: {
mainTabs: {
get() {
return this.$store.state.common.mainTabs
},
set(val) {
this.$store.commit('common/updatemainTabs',val)
}
}
},
//路由监听
watch: {
'$route': {
handler(to,from) {
if(to.path != '/home') {
this.mainTabs = to
}
this.currentNamePath = to.path
},
deep:true,
immediate:false //第一次是否要监听到
}
},
methods: {
// 点击页签
tabClick() {
this.$router.replace({ path: this.currentNamePath })
},
// 移除页签
tabRemove(namePath) {
let mainTabPathList = this.mainTabs.map(item => item.path)
let idx = mainTabPathList.indexOf(namePath)
this.mainTabs = idx
if(namePath === this.$route.path) { //删除的是当前页面 考虑路由跳转问题 不一样不需要考虑
if(!this.mainTabs.length) { //删除之后没有长度
this.$router.replace({ path: '/home' })
}else if(idx === this.mainTabs.length) { //删除的是最后一个
this.$router.replace({path: this.mainTabs[idx - 1].path})
}else {
this.$router.replace({path: this.mainTabs[idx].path})
}
}
}
}
}
</script>
<style scoped>
/* 取消标签页的magin */
/deep/ .el-tabs__header {
margin: 0!important;
}
/* 设置tab标签页 */
/deep/ .el-tabs--card > .el-tabs__header .el-tabs__nav {
height: .213542rem;
text-align: center;
line-height: .213542rem;
}
/* 设置tab标签页 */
/deep/ .el-tabs__item {
height: .208333rem;
line-height: .208333rem;
font-size: .083333rem;
font-family: 'ysbth';
}
</style>
然后下面是Vue3的实现
Vue2中的路由监听咱们可以监听route当前路由整个对象,然后拿到当前的路由
但是Vue3中也看了官网有这么一句话:
所以在Vue3中咱们尽量不要监听整个route,我也试了,确实是有问题
这里采用的是 vite2 + vue3 + pinia; 监听的路由的path,然后根据path找到当前路由对象再push进去
pinia存的状态:
import { defineStore } from 'pinia'
import viewsRouter from '@/router/views'
console.log(viewsRouter,'viewsRouter')
export const useIndexStore = defineStore('index', {
state: () => {
return {
mainTabs: []
}
},
getters: {},
actions: {
updateMainTabs(val) {
if(typeof val === 'string') {
const currentRouteObj = JSON.parse(JSON.stringify(viewsRouter[0].children.filter(item => `/${item.path}` == val)[0]))
currentRouteObj.path = `/${currentRouteObj.path}`
if(this.mainTabs.every(item => item.path !== val)) {
this.mainTabs.push(currentRouteObj)
}
}else {
this.mainTabs.splice(val, 1)
}
}
}
})
面包屑组件:
<template>
<el-tabs v-model="tabsValue" @tab-click="tabClick" @tab-remove="tabRemove" type="card" class="demo-tabs">
<el-tab-pane :label="'首页'" :name="'/index'"></el-tab-pane>
<el-tab-pane
closable
v-for="mainTab in mainTabs"
:key="mainTab.path"
:label="mainTab.meta.title"
:name="mainTab.path"
></el-tab-pane>
</el-tabs>
</template>
<script setup>
import { computed, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useIndexStore } from "@/store";
const route = useRoute();
const router = useRouter()
const indexStore = useIndexStore();
let tabsValue = ref("/index");
let mainTabs = computed({
get() {
return indexStore.mainTabs;
},
set(val) {
indexStore.updateMainTabs(val);
},
});
// 页面跳转监听
watch(
() => route.path,
(newPath, oldPath) => {
if (newPath !== "/index") {
mainTabs.value = newPath
}
tabsValue.value = newPath
},
{
deep: true
}
);
// 点击tab标签对应跳转
const tabClick = (path) => {
router.replace({
path: tabsValue.value
})
}
// 移除标签
const tabRemove = (path) => {
let mainPaths = mainTabs.value.map(item => item.path)
let idx = mainPaths.indexOf(path)
mainTabs.value = idx
if(path === route.path) { // 移除的是当前打开的页面 页面跳转问题
if(!mainTabs.value.length) {
router.replace('/index')
}else if(idx === mainTabs.value.length) {
router.replace(mainTabs.value[idx - 1].path)
}else {
router.replace(mainTabs.value[idx].path)
}
}
}
</script>
<style scoped>
</style>
Vue3中注意如果不是在模板上使用 ref响应式的数据 或 computed等时候 要使用.value才能拿到
Vue3中 watch监听的时候 如果是ref的基本数据或者reactive包裹的响应对象 第一个参数就不需要是函数的形式(这样好像拿不到旧值,旧值新值都是新值,还没确定好原因);如果要监听reactive中某一个属性 需要使用 函数的形式收集依赖才可以
watchEffect(() => {}) 会自动收集所有依赖,刚开始会触发一次,只有任意一个发生变化都会触发,只能拿到新值,拿不到旧值