实现vue公共头部组件,全局管理页面面包屑导航

lxf2023-03-15 17:32:01

实现需求

  1. 页面跳转时记录页面跳转名称
  2. 点击导航可以跳转到对应页面下
  3. 导航需要有一级二级三级页面导航
  4. 需要在某些公用页面动态修改对应页面导航名称
  5. 页面刷新页面导航不更改

实现思路分析和实现

  1. 首先要实现全局管理首先想到了使用vuex,vuex作为状态管理可以实现全局共享数据和全局修改。因为项目框架使用了vue3,所以状态管理使用了官方推荐的pina。pina语法可以看pina官网介绍,如果会用vuex基本上看一遍官网的介绍就能上手,使用起来和hooks差不多。pina的引入和注册这里就不介绍了。

  2. 因为要全局公用,所以导航就不能写死了,需要数组循环出来

import { ref } from 'vue'
export const useBreadHead = function() {
    const breadList = ref([])
    
    return {
        breadList
    }
    
}
  1. 因为需要获取对应跳转页面的名称和对应跳转路径,我首先想到的是全局路由导航守卫。但是用的时候发现在路由导航守卫触发的时候pina状态管理还没有被vue注册,无法把页面信息共享到全局中.所以只好放弃在全局路由守卫中监听.

4.不能使用全局路由守卫但是又要全局管理路由跳转怎么办呢?后来又发现组件内部可以使用beforeRouteEnterbeforeRouteUpdate来监听路由变化,所以就想到在公共组件使用局部路由守卫同样可以全局监听页面路由变化。

因为setup中没有不能监听到路由的enter,所以使用了option API

export default {
    ```
beforeRouteEnter(to,from,next) {
 
},
beforeRouteUpdate(to,from) {
  
}

}


6. 怎么才能在路由跳转的时候获取对应的页面名称呢? vue-router官方文档上有个meta属性,可以作为页面的标识符,可以在meta里加一个name,用来记录页面的名称。


```js
const routes = [
    {
        path: '/home',
        meta: {
            name: '首页‘,
        },
        componet: () => import(......)
    }
]
  1. 在页面路由改变的时候把新页面的名称和路径path放在对象里并推入breadList,但是这样每次跳转页面都会向breadList推入一个新对象,而导航只需要记录对应1级页面的2、3、4.......级导航记录,所以需要识别跳转的是几级页面。只需要在meta下面加上index来标记就可以了

  2. 当跳转的页面时1级页面时需要给breadList重新赋值,来保持导航的第一位置始终是1级页面,当页面往后跳转时需要往breadList推入导航信息,当页面从后往前跳转时(路由后退和导航点击跳转),这时候需要把旧页面的导航删除掉。因为导航页面始终是一级一级往下跳的,所以不用担心1级导航和后面的导航会出现混乱的情况。

export const useBreadHead = function() {
    const breadList = ref([])
    
function setBreadList(newVal,oldVal) {
        // //判断新老路由,如果新页面是一级页面就直接覆盖,新页面的index大于老页面直接推入,如果小于老页面就把老页面删除
        if(newVal.meta.index == 1) {
            breadList.value = [{name: newVal.meta.name,url: newVal.fullPath}]
        }else if(newVal.meta.index > oldVal?.meta.index) {
            breadList.value.push({name: newVal.meta.name,url: newVal.fullPath})
        }else if(newVal.meta.index < oldVal?.meta.index) {
            breadList.value.splice(breadList.value.length - 1,1)
        }else if(newVal.meta.index == oldVal.meta.index) {
            //删除老页面导航添加新页面导航
            breadList.value.splice(breadList.value.length - 1,1)
            breadList.value.push({name: newVal.meta.name,url: newVal.fullPath})
        }
    }
}

    
    return {
        breadList,
        setBreadList
    }
    
}

注意:导航的路径要用fullPath,因为有的页面跳转的时候需要携带query或者params参数

  1. 但是在页面刷新后oldVal是空或者是/,这时控制台就会报错,所以需要做一下判断当页面刷新后直接给breadList赋值,但是如果此时的页面是2级以上的页面时当前页面就会变成一级导航,所以要在页面跳转后给breadList保存到本地,当页面刷新后如果是2级以上的页面时直接从本地缓存里面获取。
function setBreadList(newVal,oldVal) {
    if(oldVal.path == '/') {
        if(newVal.meta.index != 1) {
            //非一级页面刷新页面从缓存里面取
            let list = getLData('breadList')
            breadList.value = list && JSON.parse(list)
        }else {
            breadList.value = [{name: newVal.meta.name,url: newVal.fullPath}]
        }
    }else {
        // //判断新老路由,如果新页面是一级页面就直接覆盖,新页面的index大于老页面直接推入,如果小于老页面就把老页面删除
        if(newVal.meta.index == 1) {
            breadList.value = [{name: newVal.meta.name,url: newVal.fullPath}]
        }else if(newVal.meta.index > oldVal?.meta.index) {
            breadList.value.push({name: newVal.meta.name,url: newVal.fullPath})
        }else if(newVal.meta.index < oldVal?.meta.index) {
            breadList.value.splice(breadList.value.length - 1,1)
        }else if(newVal.meta.index == oldVal.meta.index) {
            //删除老页面导航添加新页面导航
            breadList.value.splice(breadList.value.length - 1,1)
            breadList.value.push({name: newVal.meta.name,url: newVal.fullPath})
        }
    }
    localStorage.setItem('breadList',JSON.stringify(breadList.value))

全局组件里面监听路由:

export default {
setup() {
    const { setBreadList } = useBreadHead()
    return {
        setBreadList
    }
},
beforeRouteEnter(to,from,next) {
  next(vm => {
    vm.setBreadList(to,from)
  })
},
beforeRouteUpdate(to,from) {
  this.setBreadList(to,from)
}
}

因为beforeRouteEnter触发时不能获取this,所以要放到next

  1. 这样就可以基本实现了,但是还有一种情况,有可能一个页面可能有不同的用途,有多个入口,这时候meta里面标记的name需要做修改。但是修改route.meta.name并没有效果,因为beforeRouteUpdate会在你修改路由之前调用,所以就要用pina对共享数据进行修改。
function editBreadList(index,name) {
    breadList.value[index].name = name
    localStorage('breadList',JSON.stringify(breadList.value))
}

可以在需要做出修改的页面手动调用方法修改breadList里面对应的name

到这里就基本实现了全局导航的需求。有写的不对的地方,欢迎大家指正!