如何解决vue中避免冗余导航到当前位置错误?

Posted

技术标签:

【中文标题】如何解决vue中避免冗余导航到当前位置错误?【英文标题】:How to solve Avoided redundant navigation to current location error in vue? 【发布时间】:2020-10-09 05:40:15 【问题描述】:

我是 vue 新手,在用户登录并重定向到另一条路线后出现错误。 基本上我是一名 php 开发人员,我使用 laravel 和 vue。请帮我解决这个错误。

未捕获(承诺)错误:避免重复导航到当前位置:“/admin”。

这也是截图

Vue 代码

 methods: 
        loginUser() 
            var data = 
                email: this.userData.email,
                password: this.userData.password
            ;
            this.app.req.post("api/auth/authenticate", data).then(res => 
                const token = res.data.token;
                sessionStorage.setItem("chatbot_token", token);
                this.$router.push("/admin");
            );
        
    

Vue 路线

const routes = [
    
        path: "/admin",
        component: Navbar,
        name: "navbar",
        meta: 
            authGuard: true
        ,
        children: [
            
                path: "",
                component: Dashboard,
                name: "dashboard"
            ,
            
                path: "users",
                component: Users,
                name: "user"
            
        ]
    ,
    
        path: "/login",
        component: Login,
        name: "login"
    
];

const router = new VueRouter(
    routes,
    mode: "history"
);

router.beforeEach((to, from, next) => 
    const loggedInUserDetail = !!sessionStorage.getItem("chatbot_token");
    if (to.matched.some(m => m.meta.authGuard) && !loggedInUserDetail)
        next( name: "login" );
    else next();
);

【问题讨论】:

【参考方案1】:

除了上面提到的解决方案:一个方便的方法是把这个sn-p放到main.js中,让它成为一个全局函数


Vue.mixin(
    /**
     * Avoids redundand error when navigating to already active page
     */
    routerPush(route) 
      this.$router.push(route).catch((error) => 
        if(error.name != "NavigationDuplicated") 
          throw error;
        
      )
    ,
)

现在你可以调用任何组件了:

this.routerPush('/myRoute') 

【讨论】:

【参考方案2】:

router/index.js,初始化后


// init or import router..

// keep original function
const _push = router.__proto__.push

// then override it

// error handler
const onError = (e) => 
  if (e.name !== 'NavigationDuplicated') throw e


// avoid NavigationDuplicated
const _push = router.__proto__.push
router.__proto__.push = function push (...args) 
  try 
    const op = _push.call(this, ...args)
    if (op instanceof Promise) op.catch(onError)
    return op
   catch (e)    
    onError(e)
  


【讨论】:

【参考方案3】:

清洁解决方案;)

将代码移至 util 函数。所以你可以用它替换所有this.$router.push

import router from '../router'; // path to the file where you defined 
                                // your router

const goToRoute = (path) => 
    if(router.currentRoute.fullPath !== path) router.push(path);

export 
    goToRoute

【讨论】:

【参考方案4】:
    对于组件<router-link> 您可以创建一个新组件来代替<router-link>

组件名称:<to-link>

<template>
  <router-link :to="to" :event="to === $route.path || loading ? '' : 'click'" :class="classNames">
    <slot></slot>
  </router-link>
</template>

<script>
export default 
  name: 'ToLink',
  props: 
    to: 
      type: [String, Number],
      default: ''
    ,
    classNames: 
      type: String,
      default: ''
    ,
    loading: 
      type: Boolean,
      default: false
    
  

</script>

对于绑定:

import ToLink from '@/components/to-link'
Vue.component('to-link', ToLink)
    $router.push() 需要创建全局方法。
toHttpParams(obj) 
    let str = ''
    for (const key in obj) 
      if (str !== '') 
        str += '&'
      
      str += key + '=' + encodeURIComponent(obj[key])
    
    return str
  ,
  async routerPush(path, queryParams = undefined, params = undefined, locale = true) 
    let fullPath = this.$route.fullPath
    if (!path) 
      path = this.$route.path
    
    if (!params) 
      params = this.$route.params
    
    if (queryParams && typeof queryParams === 'object' && Object.entries(queryParams).length) 
      path = path + '?' + this.toHttpParams(queryParams)
    
   
    if (path !== fullPath) 
      const route =  path, params, query: queryParams 
      await this.$router.push(route)
    
  

【讨论】:

【参考方案5】:

聚会迟到了,但想加我的 10 美分。大多数答案 - 包括被接受的答案都试图隐藏异常而不找到导致错误的根本原因。我在我们的项目中确实遇到了同样的问题,并且感觉这与 Vue/Vue 路由器有关。最后,我设法证明自己错了,这是由于我们在 App.vue 中的代码段替换了路由以及与 index.ts 中类似的逻辑。

this.$router.replace( name: 'Login' )

因此,请尝试搜索并查找是否有任何代码调用 $router.replace 或 $router.push 来表示您担心的路线 - “/admin”。只是您的代码必须多次调用该路由,而不是 Vue 神奇地尝试多次调用它。

【讨论】:

【参考方案6】:

我通过将以下代码添加到router.js 找到了解决方案:

import router from 'vue-router'; 

const originalPush = router.prototype.push
router.prototype.push = function push(location) 
  return originalPush.call(this, location).catch(err => err)

【讨论】:

谢谢你的这段代码解决了我的问题【参考方案7】:

为了使这个完整,您还可以比较 fromto 路由 fullPaths 并将它们相互比较,这在我看来是一个简单、有效且可重用的解决方案。

这是一个组件方法中的示例:

 move(params)
    // get comparable fullPaths
    let from  = this.$route.fullPath
    let to    = this.$router.resolve(params).route.fullPath

    if(from === to)  
        // handle any error due the redundant navigation here
        // or handle any other param modification and route afterwards
        return 
    

    // route as expected
    this.$router.push(params)

如果您想使用它,只需将您的路由参数放入其中,例如 this.move( name: 'something' )。这是处理重复路由而不遇到 try catch 语法的最简单方法。您还可以在Vue.prorotype.$move = ... 中导出该方法,该方法适用于整个应用程序。

【讨论】:

谢谢。这个解决方案很简单,解决了我的问题。【参考方案8】:

短解:

  if (this.$router.currentRoute.name !== 'routeName1') 
    this.$router.push(
      name: 'routeName2',
    )
  

【讨论】:

【参考方案9】:

我遇到了这个问题,我就这样解决了。

通过在我的路由器文件中添加它

从'vue-router'导入路由器

Vue.use(路由器)

const originalPush = Router.prototype.push 
Router.prototype.push = function push(location) 
   return originalPush.call(this, location).catch(err => err)

【讨论】:

【参考方案10】:

提供 Typescript 解决方案

这个想法是覆盖router.push 函数。您可以在一个地方处理(忽略)错误,而不是到处写catch 来处理它。

这是要覆盖的函数

export declare class VueRouter 
  // ...
  push(
    location: RawLocation,
    onComplete?: Function,
    onAbort?: ErrorHandler
  ): void

这里是代码

// in router/index.ts

import Vue from 'vue';
import VueRouter,  RawLocation, Route  from 'vue-router';

Vue.use(VueRouter);

const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(location: RawLocation): Promise<Route> 
  return new Promise((resolve, reject) => 
    originalPush.call(this, location, () => 
      // on complete

      resolve(this.currentRoute);
    , (error) => 
      // on abort

      // only ignore NavigationDuplicated error
      if (error.name === 'NavigationDuplicated') 
        resolve(this.currentRoute);
       else 
        reject(error);
      
    );
  );
;

// your router configs ...

【讨论】:

谢谢你! Vue 更适合让人们提供 Typescript 示例! 很好的解决方案,虽然检查是否忽略错误应该是error.name === 'NavigationDuplicated' || error.message.includes('Avoided redundant navigation to current location') 。检查错误名称对我不起作用。【参考方案11】:

我遇到了同样的问题,当我寻找解决方案时,我发现了.catch(() =&gt; ),它实际上是在告诉浏览器我们已经处理了错误,请不要在开发工具中打印错误:) 呵呵呵呵不错的hack!但我认为忽略错误不是解决方案。所以我做了什么,我创建了一个实用函数,它接受两个参数router, path,并将其与当前路线的路径进行比较,如果两者相同,则意味着我们已经在该路线上并且它忽略了路线更改。这么简单:)

这里是代码。

export function checkCurrentRouteAndRedirect(router, path) 
  const 
    currentRoute:  path: curPath 
   = router;
  if (curPath !== path) router.push( path );


checkCurrentRouteAndRedirect(this.$router, "/dashboard-1");
checkCurrentRouteAndRedirect(this.$router, "/dashboard-2");

【讨论】:

【参考方案12】:

这意味着 - 您想要导航到与当前路线相同的路线,而 Vue 不想再次触发一切。

 methods: 
    loginUser() 
        var data = 
            email: this.userData.email,
            password: this.userData.password
        ;
        this.app.req.post("api/auth/authenticate", data).then(res => 
            const token = res.data.token;
            sessionStorage.setItem("chatbot_token", token);
            // Here you conditioally navigate to admin page to prevent error raised
            if(this.$route.name !== "/admin") 
              this.$router.push("/admin");
            
            
        );
    

【讨论】:

【参考方案13】:

你可以定义和使用这样的函数:

routerPush(path) 
  if (this.$route.path !== path) 
    this.$router.push(path);
  

【讨论】:

为什么不是内置的??应该是默认行为,然后可能会有强制推送选项。【参考方案14】:

我想这个答案来得太晚了。我没有发现错误,而是寻找一种防止错误出现的方法。因此,我通过一个名为pushSave 的附加功能增强了路由器。可能这也可以通过 navGuards 完成

VueRouter.prototype.pushSave = function(routeObject) 
  let isSameRoute = true

  if (this.currentRoute.name === routeObject.name && routeObject.params) 
    for (const key in routeObject.params) 
      if (
        !this.currentRoute.params[key] ||
        this.currentRoute.params[key] !== routeObject.params[key]
      ) 
        isSameRoute = false
      
    
   else 
    isSameRoute = false
  

  if (!isSameRoute) 
    this.push(routeObject)
  


const router = new VueRouter(
  routes
)

您可能已经意识到,这只有在您提供类似的 routeObject 时才有效

this.$router.pushSave( name: 'MyRoute', params:  id: 1  )

因此您可能需要对其进行增强以使其也适用于字符串

【讨论】:

【参考方案15】:

我在项目的main.js文件中添加了以下代码,错误消失了。

import Router from 'vue-router'
const routerPush = Router.prototype.push
Router.prototype.push = function push(location) 
return routerPush.call(this, location).catch(error => error)
;

【讨论】:

【参考方案16】:

这可能是因为您正在尝试路由到现有的$route.matched.path

原始海报

您可能希望通过阻止指向同一路径的路由来防止错误:

if (this.$route.path != '/admin') 
    this.$router.push("/admin");

通用解决方案

如果您正在发送动态路由,您可以创建一个方法来检查这一点,使用几个选项之一

    简单:忽略错误 困难:将$route.matched 与所需路线进行比较

1。忽略错误

您可以捕获NavigationDuplicated 异常并忽略它。

pushRouteTo(route) 
    try 
        this.$router.push(route);
     catch (error) 
       if (!(error instanceof NavigationDuplicated)) 
           throw error;
       
    

虽然这要简单得多,但它让我很困扰,因为它会产生异常。

2。将$route.matched 与所需路线进行比较

您可以将$route.matched 与所需路线进行比较

pushRouteTo(route) 
    // if sending path:
    if (typeof(route) == "string") 
        if (this.$route.path != route) 
            this.$router.push(route);
        
     else  // if sending a name: '', ...
        if (this.$route.name == route.name) 
            if ('params' in route) 
                let routesMatched = true;
                for (key in this.$route.params) 
                    const value = this.$route.params[key];
                    if (value == null || value == undefined) 
                        if (key in route.params) 
                            if (route.params[key] != undefined && route.params[key] != null) 
                                routesMatched = false;
                                break;
                            
                        
                     else 
                        if (key in route.params) 
                            if (routes.params[key] != value) 
                                routesMatched = false;
                                break
                            
                         else 
                            routesMatched = false;
                            break

                        
                    
                    if (!routesMatched) 
                        this.$router.push(route);
                    
                
             else 
                if (Object.keys(this.$route.params).length != 0) 
                    this.$router.push(route);
                
            
        
    

这显然要长得多,但不会引发错误。选择你的毒药。

可运行的演示

您可以在此演示中尝试这两种实现:

const App = 
  methods: 
    goToPageCatchException(route) 
      try 
        this.$router.push(route)
       catch (error) 
        if (!(error instanceof NavigationDuplicated)) 
          throw error;
        
      
    ,
    goToPageMatch(route) 
    if (typeof(route) == "string") 
        if (this.$route.path != route) 
            this.$router.push(route);
        
     else  // if sending a name: '', ...
        if (this.$route.name == route.name) 
            if ('params' in route) 
                let routesMatched = true;
                for (key in this.$route.params) 
                    const value = this.$route.params[key];
                    if (value == null || value == undefined) 
                        if (key in route.params) 
                            if (route.params[key] != undefined && route.params[key] != null) 
                                routesMatched = false;
                                break;
                            
                        
                     else 
                        if (key in route.params) 
                            if (routes.params[key] != value) 
                                routesMatched = false;
                                break
                            
                         else 
                            routesMatched = false;
                            break

                        
                    
                    if (!routesMatched) 
                        this.$router.push(route);
                    
                
             else 
                if (Object.keys(this.$route.params).length != 0) 
                    this.$router.push(route);
                
            
         else 
          this.$router.push(route);
        
    
    ,
  ,
  template: `
  <div>
    <nav class="navbar bg-light">
          <a href="#" @click.prevent="goToPageCatchException(name:'page1')">Catch Exception</a>
          <a href="#" @click.prevent="goToPageMatch(name:'page2')">Match Route</a>
    </nav>
    <router-view></router-view>
  </div>`

const Page1 = template: `
  <div class="container">
    <h1>Catch Exception</h1>
    <p>We used a try/catch to get here</p>
  </div>`

const Page2 = template: `
  <div class="container">
    <h1>Match Route</h1>
    <p>We used a route match to get here</p>
  </div>`


const routes = [
   name: 'page1', path: '/', component: Page1 ,
   name: 'page2', path: '/page2', component: Page2 ,
]

const router = new VueRouter(
  routes
)

new Vue(
  router,
  render: h => h(App)
).$mount('#app')
<link href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.12/vue.min.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>

<div id="app"></div>

【讨论】:

您可以将完整路径与用作this.$router.resolve(params).route.fullPath 的路由器解析器进行比较。这将为您提供带有查询参数的完整路径,然后您可以将 === 与当前的 this.$route.fullPath 进行比较。【参考方案17】:

我记得很清楚,你可以在 this.$router.push 之后使用 catch 子句。然后它看起来像:

this.$router.push("/admin").catch(()=>);

这允许您只避免显示错误,因为浏览器认为异常已被处理。

【讨论】:

这个可以扩展为if (this.$route.name !== 'navbar') this.$router.push("/admin").catch(()=&gt;); 不解决问题,只避免显示任何问题改变路线 如果这是调用push() 可能产生的唯一可能的有意义的错误,那么当然......但事实并非如此,因此您将抑制来自那里的每条错误消息。这不应该是公认的答案。 太棒了! tnks这么多 这是错误的答案,因为它不能解决 @EmilianoPamont 所说的隐藏的问题。【参考方案18】:

我认为抑制路由器的所有错误并不是一个好习惯,我只是挑选了某些错误,如下所示:

router.push(route).catch(err => 
    // Ignore the vuex err regarding  navigating to the page they are already on.
    if (
      err.name !== 'NavigationDuplicated' &&
      !err.message.includes('Avoided redundant navigation to current location')
    ) 
      // But print any other errors to the console
      logError(err);
    
  );

【讨论】:

以上是关于如何解决vue中避免冗余导航到当前位置错误?的主要内容,如果未能解决你的问题,请参考以下文章

VUE router 导航重复点击报错的问题解决方案

如何在Angular的当前页面中执行某些操作时避免导航到另一个页面

如何在默认导航一个 Geopoint 中设置用户当前位置?

非关系数据库中如何避免冗余数据?

非关系数据库中如何避免冗余数据?

如何避免在浏览器 JavaScript 库中包含冗余的 Promise 实现?