Vue Router的NavigationDuplicated错误

博客主要围绕Vue中NavigationDuplicated错误展开,提到posva高赞回答的解决方案。考虑到开发环境需查看错误信息,给出了一些修改方案,包括自定义开发模式启动语句添加dev模式语句、自定义日志打印及在开发者模式下打印日志。
  • NavigationDuplicated: Avoided redundant navigation to current location: “/”.
  • Error: Redirected when going from “" to "” via a navigation guard.

Vue router最近将NavigationDuplicated的错误置顶了,posva的高赞回答提出的解决方案如下:

import Router from 'vue-router'

const originalPush = Router.prototype.push
Router.prototype.push = function push(location, onResolve, onReject) {
  if (onResolve || onReject)
    return originalPush.call(this, location, onResolve, onReject)
  return originalPush.call(this, location).catch((err) => {
    if (Router.isNavigationFailure(err)) {
      // resolve err
      return err
    }
    // rethrow error
    return Promise.reject(err)
  })
}

但开发环境下需要查看错误信息,因此可以做一些小修改。

1 自定义开发模式启动语句,添加dev模式语句package.json

{
  "scripts": {
    "dev": "NODE_ENV=development vue-cli-service serve",
  },
}

2 自定义日志打印,创建Log.js

/**
 * 转换为html的style
 * @param {Object} style 
 */
const styleTranslate = (style) => {
  return Object.entries(style)
    .map(([key, value]) => `${key}:${value}`)
    .join(';')
}
const style = {
  'color': '#FFFFFF',
  'padding': '4px',
  'border-radius': '4px',
}
const STYLE_DEV_ERROR = styleTranslate({
  'background-color': '#FF0000',
  ...style
})
// ...

export default {
  // ...
  /**
   * 如果当前的NODE_ENV是development模式,则向控制台输出错误信息
   *
   * @param {*} callback
   */
  devError(...message) {
    if (process.env.NODE_ENV === 'development') {
      console.error('%cdevError', STYLE_DEV_ERROR, ...message)
    }
  },
}

3 开发者模式下打印日志Log.devError(err)

import Router from 'vue-router'
import Log from './Log'

const originalPush = Router.prototype.push
Router.prototype.push = function push(location, onResolve, onReject) {
  if (onResolve || onReject)
    return originalPush.call(this, location, onResolve, onReject)
  return originalPush.call(this, location).catch((err) => {
    Log.devError(err)
    if (Router.isNavigationFailure(err)) {
      // resolve err
      return err
    }
    // rethrow error
    return Promise.reject(err)
  })
}
<think>好的,我现在需要帮助用户了解如何用TypeScript重写Vue Router的push方法。首先,我需要回顾一下Vue Router的基本知识,特别是版本4,因为用户提到的引用中提到了vue-router@4。根据引用[3],用户已经安装了vue-router@4,并且项目是基于Vue3和TypeScript的,所以应该考虑Vue3的Composition API和TypeScript的类型支持。 接下来,用户想重写push方法。通常,重写push方法可能是为了添加一些全局的逻辑,比如导航守卫、错误处理或者自定义参数。Vue Router允许通过扩展或覆盖原有的push方法来实现这一点。需要确保在TypeScript中正确声明类型,避免类型错误。 根据Vue Router的文档,push方法的原始类型定义为:(to: RouteLocationRaw) => Promise<NavigationFailure | void>。因此,在重写时,需要保持相同的参数和返回类型。用户可能需要创建一个新的push方法,调用原始的push方法,并在调用前后添加自定义逻辑,比如错误处理或日志记录。 例如,用户可能希望统一处理导航错误,比如在遇到导航中止时显示错误信息。这时候可以在自定义的push方法中捕获错误,并根据情况进行处理。同时,在TypeScript中,需要确保传入的参数符合RouteLocationRaw类型,这可能包括路径字符串、带有name或path的对象等。 另外,用户可能需要扩展push方法以支持自定义参数,比如添加一个参数来跳过某些全局处理。这时候需要使用TypeScript的函数重载来声明不同的参数组合,确保类型安全。例如,定义一个包含额外选项的接口,并在函数重载中声明这些参数类型。 还需要注意在Vue3中,Vue Router的实例是通过createRouter创建的,因此需要确保在创建路由实例后,替换或扩展其push方法。这可能涉及到获取原始的push方法引用,然后将其替换为自定义的函数,同时保持this上下文的正确性。 此外,引用中提到的动态路由、导航守卫(如onBeforeRouteUpdate和onBeforeRouteLeave)可能相关,但用户的问题主要集中在push方法的重写上。需要确保在重写push方法时,不影响其他路由功能,比如导航守卫的正常触发。 最后,需要测试重写后的push方法,确保在TypeScript项目中类型检查通过,并且功能符合预期。可能还需要提供示例代码,展示如何定义类型、重写方法,并在组件中使用新的push方法。</think>### 如何用 TypeScript 重写 Vue Router 的 push 方法? #### 1. **理解原始 push 方法的类型定义** Vue Router v4 的 `push` 方法类型声明为: ```typescript push(to: RouteLocationRaw): Promise<NavigationFailure | void> ``` 其中 `RouteLocationRaw` 可以是路径字符串或包含路由参数的对象(如 `{ name: 'routeName', query: {...} }`)[^3]。 --- #### 2. **扩展 push 方法的基本步骤 **步骤 1:创建自定义 push 函数** ```typescript // router/index.ts import { createRouter } from 'vue-router' const router = createRouter({ /* 配置 */ }) const originalPush = router.push // 重写 push 方法 router.push = function (to: RouteLocationRaw) { return originalPush.call(this, to) .catch(error => { if (error.name !== 'NavigationDuplicated') { console.error('导航失败:', error) } return Promise.reject(error) }) } ``` --- #### 3. **添加自定义参数(高级场景)** 如果需要支持额外参数(如跳过权限校验): ```typescript // 声明扩展类型 declare module 'vue-router' { interface RouteLocationOptions { skipAuthCheck?: boolean } } // 重载函数类型 router.push = function ( to: RouteLocationRaw | (RouteLocationRaw & { skipAuthCheck?: boolean }) ): Promise<NavigationFailure | void> { const options = typeof to === 'object' ? to : {} if (options.skipAuthCheck) { console.log('跳过权限校验') } return originalPush.call(this, to) } ``` --- #### 4. **完整 TypeScript 实现示例** ```typescript // router/index.ts import { createRouter, type RouteLocationRaw, type NavigationFailure } from 'vue-router' const router = createRouter({ history: createWebHistory(), routes: [...] }) // 保存原始方法引用 const originalPush = router.push.bind(router) // 重写 push 方法 router.push = function ( this: typeof router, to: RouteLocationRaw ): Promise<NavigationFailure | void> { // 添加预处理逻辑 if (typeof to === 'string') { console.log('导航到路径:', to) } else if (to.name) { console.log('导航到命名路由:', to.name) } // 执行原始导航并处理错误 return originalPush(to) .catch((error) => { if (error.name === 'NavigationDuplicated') { console.warn('重复导航已被阻止') } else { console.error('导航异常:', error) } return Promise.reject(error) }) } export default router ``` --- #### 5. **在组件中使用** ```typescript // Component.vue import { useRouter } from 'vue-router' const router = useRouter() // 基础用法 router.push('/home') // 带参数用法 router.push({ name: 'user', params: { id: 123 }, query: { from: 'dashboard' } }) ``` --- ### 关键注意点 1. **类型安全**:通过 `RouteLocationRaw` 确保路由参数类型正确 2. **错误处理**:必须捕获 `NavigationFailure` 错误[^1] 3. **上下文绑定**:使用 `.call(this, ...)` 保持正确的 `router` 实例上下文 4. **模块扩展**:使用 TypeScript 声明合并扩展路由参数类型 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值