Vue3中路由监听(类似面包屑的实现)

3 篇文章 0 订阅

这里举个例子:就是类似于面包屑,会记录所有的打开过的页面,方便定位到某个页面。(因为这个例子核心就是路由监听)

思路:

首先首页会一直显示,不可以移除。 我这里把其他的路由对象存在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(() => {})  会自动收集所有依赖,刚开始会触发一次,只有任意一个发生变化都会触发,只能拿到新值,拿不到旧值

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会说法语的猪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值