首先看一下声明式导航与编程式导航
声明式导航:
声明式导航是写在template
标签里,通过<router-link></router-link>
标签来触发:
<router-link to="/about">跳转</router-link>
编程式导航:
编程式导航是在js代码进行跳转的操作。使用this.$router.push(xxx)
来触发跳转:
this.$router.push('path')
//传递参数
this.$router.push({
path: 'path', query: {
id: 1}})
this.$router.push({
name: 'name', params: {
id: 1}})
问题
编程式路由跳转到当前路由(参数不变),多次执行会抛出 NavigationDupLicated 的警告错误。声明式导航则不存在此问题,因为`vue-router`底层已经处理好了。
为什么编程式导航进行路由跳转的时候,就会有这种警告错误呢?因为 vue-router:3.5.3 版本引入了 promise
,假如没有回调函数,错误信息就会交给全局的路由错误处理,因此就会报上述的错误。
解决方法
方法一 运用编程式路由的时候加上成功,失败的回调
this.$router.push(
{ name: "home", params: { id: 1 } },
() => {}, // 成功回调
() => {} //失败回调
);
方法二 重写 push,replace
方法一治标不治本,每次用编程式路由都要加成功失败回调
下面通过重写 push,replace 解决:$router
是 vueRouter
类的一个实例push
方法在 vueRouter
的 prototype
上
重写push
// 在路由index.js文件中
// 重写push
// 保存原有的push方法
const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function (location, resolve, reject) {
// 如果用户传递过来了成功失败回调
// 调用原有push方法
if (resolve && reject) {
//这里要注意上下文中的this为vueRouter 这里如果直接调用 originalPush 中的this是window,我们要将this改为vueRouter,这时就要用this冒充call
originalPush.call(this, location, resolve, reject);
} else {
// 用户没有传递成功失败回调,我们自动加上
originalPush.call(this,location,() => {},() => {});
}
};
重写replace
// 在路由index.js文件中
// 重写replace
// 保存原有的replace方法
const originalReplace = VueRouter.prototype.replace;
VueRouter.prototype.push = function (location, resolve, reject) {
if (resolve && reject) {
originalReplace.call(this, location, resolve, reject);
} else {
originalReplace.call(this,location,() => {},() => {});
}
};
一个完整的 路由文件
src/router/index.js
// 该文件专门用于创建整个应用的路由器
// 引入vue路由
import VueRouter from "vue-router";
import routers from "./routers";
// 重写push和replace
let originPush = VueRouter.prototype.push;
let originReplace = VueRouter.prototype.push;
// push方式跳转路由(有痕)
VueRouter.prototype.push = function (location, resolve, reject) {
if (resolve && reject) {
originPush.call(this, location, resolve, reject);
} else {
originPush.call(this, location, () => {}, () => {});
}
};
// replace方式跳转路由(无痕)
VueRouter.prototype.replace = function (location, resolve, reject) {
if (resolve && reject) {
originReplace.call(this, location, resolve, reject);
} else {
originReplace.call(this, location, () => {}, () => {});
}
};
import store from "@/store";
//创建并暴露一个路由器
let router = new VueRouter({
routes: routers,
// 滚动行为
scrollBehavior(to, from, savedPosion) {
//返回的这个y=e,代表的滚动条在最上方
return {y: 0};
},
});
//全局守卫:前置守卫(在路由跳转之间进行判断)
router.beforeEach(async (to, from, next) => {
// to and from are both route objects. must call `next`.
//获取仓库中的token-----可以确定用户是登录了
let token = store.state.user.token;
let name = store.state.user.userInfo.name;
//用户登录了
if (token) {
//已经登录而且还想去登录------不行
if (to.path == "/login" || to.path == "/register") {
next("/home");
} else {
//已经登陆了,访问的是非登录与注册
//登录了且拥有用户信息放行
if (name) {
next();
} else {
//登陆了且没有用户信息
//在路由跳转之前获取用户信息且放行
try {
//获取用户信息
await store.dispatch("getUserInfo");
next();
} catch (error) {
//token失效从新登录
//清除token
await store.dispatch("logout");
// 回到登录页
this.$router.push("/login");
}
}
}
} else {
//未登录:不能去交易相关、不能去支付相关【pay|paysuccess】、不能去个人中心
//未登录去上面这些路由-----登录
let toPath = to.path;
if (toPath.includes("/trade") || toPath.includes("/pay") || toPath.includes("/center")) {
//把未登录的时候向去而没有去成的信息,存储于地址栏中【路由】
next("/login?redirect=" + toPath);
// console.log(toPath);
} else {
//去的不是上面这些路由(home|search|shopCart)---放行
next();
}
}
});
export default router;
src/router/routers.js
export default [
{
path: "/paySuccess",
component: () =>
import ("@/pagaes/PaySuccess"),
meta: {
show: true,
},
},
]