$router 路由的集合

借鉴文章:

动态路由的使用

  1. 路由对象属性
    - $route.path:当前路由的路径,总是解析为绝对路径,如 /foo/bar (string)
    - $route.params:key/value 对象,包含动态片段和全匹配片段,没有则为{}
    - $route.query:key/value 对象,表示 URl 参数,例如:/foo?user=1 即 route.query.user==1
    - $route.hash:当前路由的 hash 值(带#),没有则为空字符串
    - $route.fullPath:完成解析后的 URl,包含查询参数 hash 的完整路径
    - $route.matched:Array,包含当前路由的所有嵌套的路由记录,其中包含 mate: 元信息的集合
    - $route.name:当前路由的名称
    - $route.redirectedFrom:如果存在重定向,即为重定向来源的路由的名字

  2. 路由跳转
    2.1 声明式导航 <router-link :to="页面路径"></router-link>
    eg:

```js
  // 没有传参时的跳转
  <router-link :to="/index">测试页面</router-link>
  // 传递参数, 和url拼接参数时一样都是拼接参数在后面
  <router-link :to="/index?id=23">测试页面</router-link>

  //跳转后页面获取参数
  this.$route.params.id
```

2.2. vue-router里面的配置

```js
{
    path: '/Descr/:id',
    name: 'Descr',
    component: Descr
}


// 跳转时页面
var id = 1;
this.$router.push('/Descr/' + id)

//跳转后页面获取参数
this.$route.params.id
```



2.3. 通过query属性传值(url中不显示参数)

  ```js

  //跳转时页面
  this.$router.push({
    path: '/Descr',
    query: {
      name: '李四',
      id: 3,
    }
  })
  
  //跳转后页面获取参数对象
  this.$route.query 
  ```

2.4. 通过params属性传值(url中显示参数)

```js
//跳转时页面
this.$router.push({
  name: '/Descr',
  params: {
    name: '李四',
    id: 1,
  }
})

//跳转后页面获取参数对象
this.$route.params

///父路由组件上使用router-link进行路由导航,传参用<router-link to="/one/login/001">的形式向子路由组件传递参数。使用router-view进行子路由页面内容渲染
```

总结:

  1. 动态路由和query属性传值 页面刷新参数不会丢失, params会丢失 

  2. 动态路由一般用来传一个参数时居多(如详情页的id), query、params可以传递一个也可以传递多个参数

  注意:**如果在Vue项目中传递的参数是一整个对象可以使用JSON.stringify将参数转换一下**

  ```js
  // 跳转路由传递对象参数
  var arr=JSON.stringify(this.对象)
  this.$router.push('/Descr'+encodeURIComponent(arr))
  ```

  >接收参数的时候,先用decodeURIComponent()将传递过来的参数转换一下,然后再用JSON.parse再次转换,这样,一个对象就完整的传递过来了,

  ```js
        // 获取传过来的参数
        var list = decodeURIComponent(this.$route.params.obj);
        this.对象 = JSON.parse(list);
  ```
  注意:**文件流数据file不能使用这个方法,因为file数据一用JSON来转换,就为空了,所以也不能用本地存贮,但可以使用vuex来保存**
  1. 路由的渲染

可以通过 标签去渲染

  1. hash 路由和 history 路由的区别

一般默认的vue路由就是hash模式,hash模式的路由:地址栏里面的url会带有#形式的,如果想要切换成history模式时,可以在router里的index.js里面,创建路由实例时给修改了

  const router = new VueRouter({
    // 更改为history模式
    mode:'history'
  })

顶部面包屑的编写

  1. 编写面包屑组件
    1. 借鉴使用 elment ui 组件的面包屑
    2. 面包屑组件路径:src/components/BreadCrumbs/index.vue
    3. 动态路由编写

记录动态路由遇到的坑

关于 router 动态添加路由和菜单所遇到的坑和解决方法,记录以防止下次遇到

  1. 在 store 里面对获取的路由信息进行处理
import layout from "@/views/layout";

const menu = {
  //初始对象(和data类似)--放置要用到的值
  state: {
    routers: [],
    addRoutes: [],
  },
  //将值赋予给state
  mutations: {
    //获取路由信息
    SET_ROUTERS: (state, routers) => {
      state.routers = routers;
    },
    //获取拼装好的路由路径
    SET_ADD_ROUTERS: (state, addRoutes) => {
      state.addRoutes = addRoutes;
    },
  },
  //异步回调事件,操作mutations里的事件赋值给state
  actions: {
    //获取路由信息
    getRouters({ commit }, menuData) {
      commit("SET_ROUTERS", menuData.data);

      let menu = filterChildren(menuData.data);
      //添加路由
      commit("SET_ADD_ROUTERS", menu);
      //左侧菜单路由
      commit("SET_SIDEBAR_ROUTERS", menu);
    },
  },
};

//拼接路由
function filterChildren(router) {
  //循环遍历拼接路由
  return router.filter((route) => {
    //判断是否含有下级路由
    if (route.children && route.children.length > 0) {
      route.component = layout;
      //再次循环
      route.children = filterChildren(route.children);
    } else {
      route.component = loadView(route.component);
    }
    return true;
  });
}

// 路由路径拼接
function loadView(view) {
  // 路由懒加载
  return () => import(`@/views/${view}`);
}
export default menu;
  1. 创建 permission.js 在根目录下,在 main.js 里面引用

    import NProgress from "nprogress";
    import store from "./store";
    import router from "./router";
    
    const NoLoginList = ["/iframe"]; //免登录页面
    import menuData from "@/assets/model/menuData.json";
    
    router.beforeEach(async (to, from, next) => {
      // to 路由当前路径,可以获取当前路由的信息
      // from 要离开去的路由页面,可以获取当前路由的信息
      // next 放过拦截,执行下一个页面跳转
      // 得到本地存储的token  想要进其他页面,就要带token
      let token = store.state.user.token;
      if (token) {
        //有登录并在登录页,直接进入首页
        if (to.path === "/login") {
          next("/");
        } else {
          //非登录页直接放过
          //判断是否是初次进入渲染动态路由,当跳转其他路由时不要再次添加和获取
          //模拟后台请求数据
          await store.dispatch("getRouters", menuData);
    
          router.addRoutes(store.state.menu.addRoutes);
    
          // 这一行就是解决问题的最终办法, 重新进当前路由
          next();
        }
      } else {
        //未登录,如果是免登录页面直接进入
        if (NoLoginList.indexOf(to.path) !== -1) {
          // 获取重定向页面路径
          next();
        } else if (to.path === "/login") {
          //多加一步判断是否是去登录,以免进入路由死循环里
          next();
        } else {
          //非登录页,也不是免登录页就跳转到登录页
          next({ path: "/login", query: { redirect: to.fullPath } });
        }
      }
    });
    
    • 错误信息
     [vue-router] router.addRoutes() is deprecated and
     has been removed in Vue Router 4. Use router.addRoute() instead.
    

    原因:addRoutes()已弃用,并且已在 Vue 路由器 4 中删除。
    解决:改为 addRoute()就可以了

  2. 记录报错内容

  • 使用 router.addRoute()
  router.beforeEach(async (to, from, next) => {
    //其他代码省略
    router.addRoute(store.state.menu.addRoutes);
    //其他代码省略
  });
  • 运行报错信息

错误:[vue路由器]“路径”在路由配置中是必需的
Uncaught (in promise) Error: [vue-router] “path” is required in a route configuration.

原因:添加一条新的路由规则记录作为现有路由的子路由。不能直接添加数据类型的路由信息,需要一条一条的添加
如果该路由规则有 name,并且已经存在一个与之相同的名字,则会覆盖它
解决:一条一条添加就行 2. 使用 router.addRoute()

解决代码

  router.beforeEach(async (to, from, next) => {
    //其他代码省略
    store.state.menu.addRoutes.forEach((item) => {
      router.addRoute(item);
    });
    next();
    //其他代码省略
  });
  • 运行报错信息:页面出现空白

    在路由的前置守卫里面, 使用 addRoutes 钩子后, 直接调用 next()
    如果当前页面的 路由 是通过 addRoutes 添加进去的 刷新页面就会出现
    对应的路由组件不会渲染的情况, 也不会报错,页面会显示为空白
    详细原因未知: 大致理解为, 当进入路由的前置钩子 (router.beforEach) 的时候,
    路由的结构是不会发生变化的, 至少本次跳转, 路由的结构是不会变化的
    也就是相当于我们举报一次活动,明天能看,但我们今天就要看一样,所以也就不能使用了 3. 使用 next({…to, replace: true})解决页面空白问题

      router.beforeEach(async (to, from, next) => {
        //其他代码省略
        store.state.menu.addRoutes.forEach((item) => {
          router.addRoute(item);
        });
        // 这一行就是解决空白问题的最终办法, 重新进当前路由
        next({ ...to, replace: true });
        //其他代码省略
      });
    

    运行报错信息:加入这行代码会造成页面陷入死循环,每次都去添加,重进当前路由,
    解决方法:只使用一次:在初次加载时添加路由,其他页面路由跳转不在添加 4. 加入 isAddRouter 判断是否是初次加载动态路由

      let isAddRouter = false;
      router.beforeEach(async (to, from, next) => {
        //其他代码省略
        //判断是否是初次进入渲染动态路由,当跳转其他路由时不要再次添加和获取
        if (!isAddRouter) {
          store.state.menu.addRoutes.forEach((item) => {
            router.addRoute(item);
          });
          // 这一行就是解决空白问题的最终办法, 重新进当前路由
          next({ ...to, replace: true });
        } else {
          next();
        }
        //其他代码省略
      });
    
  1. 最终 permission.js 代码
  //当导航触发时,前置路由守卫会创建顺序,异步解析执行获取路由信息
  import NProgress from "nprogress";
  import store from "./store";
  import router from "./router";

  const NoLoginList = ["/iframe"]; //免登录页面
  import menuData from "@/assets/model/menuData.json";

  let isAddRouter = false;

  router.beforeEach(async (to, from, next) => {
    // to 路由当前路径,可以获取当前路由的信息
    // from 要离开去的路由页面,可以获取当前路由的信息
    // next 放过拦截,执行下一个页面跳转
    // 得到本地存储的token  想要进其他页面,就要带token
    NProgress.start();
    let token = store.state.user.token;
    if (token) {
      //有登录并在登录页,直接进入首页
      if (to.path === "/login") {
        next("/");
      } else {
        //非登录页直接放过
        //判断是否是初次进入渲染动态路由,当跳转其他路由时不要再次添加和获取
        if (!isAddRouter) {
          //模拟后台请求数据
          await store.dispatch("getRouters", menuData);
          //渲染之后改变状态,截止动态路由的再次渲染
          isAddRouter = true;
          // router.options.routes里的数据需要手动添加,
          // router.addroutes/router.addroute添加的数据是不会自动到这里面的
          router.options.routes = [...store.state.menu.addRoutes];
          store.state.menu.addRoutes.forEach((item) => {
            router.addRoute(item);
          });

          // 这一行就是解决问题的最终办法, 重新进当前路由
          next({ ...to, replace: true });
        } else {
          next();
        }
      }
      NProgress.done();
    } else {
      //未登录,如果是免登录页面直接进入
      if (NoLoginList.indexOf(to.path) !== -1) {
        // 获取重定向页面路径
        next();
      } else if (to.path === "/login") {
        //多加一步判断是否是去登录,以免进入路由死循环里
        next();
      } else {
        //非登录页,也不是免登录页就跳转到登录页
        next({ path: "/login", query: { redirect: to.fullPath } });
      }
      NProgress.done();
    }
  });

解决 vue 路由跳转未匹配路径时出现空白页的问题

借鉴文章:
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于 等待中。

每个守卫方法接收三个参数:

to: Route: 即将要进入的目标 路由对象

from: Route: 当前导航正要离开的路由

next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。

next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。

next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。

next(‘/’) 或者 next({ path: ‘/’ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: ‘home’ 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。

next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。

确保要调用 next 方法,否则钩子就不会被 resolved。

  1. 在前置路由内配置,匹配路由
router.beforeEach(async (to, from, next) => {
    /*
        初次进入页面后,渲染动态路由后,重定向页面,会再走一边前置路由
        在这一步处理未匹配相应用路由避免出现空白页面的问题
    */
    //判断是否匹配到路由
    if (to.matched.length === 0) {
    //第一种情况: 404页面内容可以自定义 from.path ? next({ path: from.path }) : next({ path: "/404", redirect: "/404", hidden: true })
    //第二种情况: 未自定义页面直接跳转到首页 from.path ? next({ path: from.path }) : next("/")
    from.path ? next({ path: from.path }) : next("/");
    } else {
    next(); //有匹配到页面,跳转正确页面
    }
}