Problem: this.$router.currentRoute can not get current route

In App.vue, inside mounted() hook, I want to use currentRoute to determine if its a ‘launch’ route to do something. But I found I can’t get the correct value, and console.log() them as below:

mounted() {
  console.info('App this router:', this.$router)
  console.info('App currentRoute:', this.$router.currentRoute)
}

Then if i visit /#/launch, in the browser console, I found this.$router.currentRoute was always an object point to root path ‘/’:
{name: null, meta: {…}, path: “/”, hash: “”, query: {…}}

not the correct path ‘/launch’ or route.name, and the same value with this.$router.history.current

But I also found there is a property called ‘pending’ under this.$router.history which possesses the correct current route like:
{name: “Launch”, meta: {…}, path: “/launch”, hash: “”, query: {…}, …}

I don’t know why I can’t get correct route, I searched on google&baidu, seems no one have met that problem. Cound anyone give advice?

version:
I’v updated to latest Vue 2.5.16 and Vue-router 2.8.1

1 Like

A little bit more information would be helpfull, like:

  • Why don’t you use this.$route?
  • What routes do you have defined?
  • Where do you render App.vue? as the root component “around” the first <router-view>?
  • Do you use any transitions?
  • Can you replicate a small example on e.g. jsfiddle.net?

My gut reaction is that App.vue is rendered before the router has finished the “initial” routing, because App.vue (if used as I assumed above) does not depend on the route being loaded correctly.

You can probably use awatch: on the $route object to do something as soon as the desired route is finished loading.

@LinusBorg

  • Why don’t you use this.$route?
    Yes, I tested this.$route, it is the same incorrect result wih pah ‘/’

  • What routes do you have defined?
    A lot of routes defined, nearly about 50

  • Where do you render App.vue? as the root component “around” the first ?
    The project originally built from a quasar framework project, it seems App.vue is “around” first . See code below:

Quasar.start(() => {
  /* eslint-disable no-new */
  new Vue({
    el: '#q-app',
    store, // inject store to all children
    router,
    computed: {
      ...
    },
    methods: {
      ...
    },
    created () {
      ...
    },
    render: h => h(require('./App'))  //<--- here is App.vue
  });
});
  • Do you use any transitions?
    I’m not sure, maybe, because the project is based on Quasar framework, I know little about it.

  • Can you replicate a small example on e.g. jsfiddle.net?
    Sorry, i can’t. The project is not my private, it’s company’s and it’s too big. I try to reproduce it in a simple vue project, but I failed, this.$route and this.$router.currentRoute can get correct result.

Maybe it’s not proper to ‘watch’ this.$route, because what I need to do is to invoke an authentication function only once at vue app’s start up time, depending on the current route.

I would only be interested in the ones involved in the problem, namely / and ‘launch’, trying to understand their relationship - is it a subroute, it that subroute lazy-loaded? etc.pp.

I wouldn’t ask for the projects source code, but for an example that illustrates the problem so I can be sure to understand it. If you can’t build one, that’s unfortunate but we can try to go without one.

You can construct a one-time watcher like this:

  this.routeWatcher = this.$watch(
    function () {  return this.$route },
    function(route) {
      if (route.name === 'launch') {
        this.authenticate() // or whatever
        this.routeWatcher() // this disables the watcher
      }
    }
  )
}

There may be a better solution, but I can’t provide one without having a better idea of the real problem.

@LinusBorg
I made a demo that can reproduce the problem, i believe something inside quasar cause the startup process different from normal vue project, you can check it from:


After running it, open web console, and visit: http://localhost:8080/#/launch to see the log.

In fact, my requirement is a little different:
If the route is ‘Launch’, skip this.authenticate();
If the route is not ‘Launch’, do this.authenticate();

I think I can modify your code like this (I haven’t tested):

  this.routeWatcher = this.$watch(
    function () {  return this.$route },
    function(route) {
      if (route.name !== 'launch') {
        this.authenticate() // or whatever
      }
      this.routeWatcher() // this disables the watcher
    }
  )

Thanks for the code of ‘construct a one-time watcher’!

Sounds like a usual case for a router navigation guard. Have you read an it those in the docs? I think there is even an example about authentication.

@flight9 I believe you want to make use of meta https://router.vuejs.org/en/advanced/meta.html

@JamesThomson & @LinusBorg Thanks for doc link, I read it and find my situation is different.

Our this.authenticate() function should be invoked only once on vue app startup and it’s an async function not sync. I feel it’s not suitable for route guard to be called many times.

The reason why I skip this.authenticate() in ‘Launch’ route is not because the ‘Launch’ route do not need to be protected by authentication, but I hope to call this.authenticate() singly inside Launch.vue itself.

The Launch, as the only entry for wechat, waits for the result of this.authenticate(), and if fails, it should start the process of OAuth2.0. While the other routes do not care about authenticate’s result.

So I will meet the problem mentioned at the top of this topic.

I disagree. It’s one of the most common use case a for a navigation guard.

@LinusBorg
Sorry, for my poor expression. I agree with you about using meta and route guard and it’s a comman use case.
Let me correct that:

I feel it’s not suitable for my authenticate() to be called many times in route guard.

Well you don’t have to. You can of course establish some kind of check that skips authentication when it was already done.

Hi guys, I am now facing following case: https://jsfiddle.net/L7hscd8h/18641/

Any ideas?

Simply put - I want to push new route which is exactly the same as current route. I need to listen to this in the component and refresh the data.
Real world scenario is different but this case is a good representation - I just need to update the “count” in the component every time the menu item is clicked.

PS: I might be overworked but when I refresh in Chrome I am getting two different types of behaviour depending on which menu item I click first … weird … I hope someone will be able to help … :slight_smile:

is correctly imported?

e.g.

import Router from 'vue-router'

can you check if router.currentRouter.path === ‘/detail/:name’ ?

The router won’t do anything if it’s the same route, why should it?

You need to find another way to achieve that count…

If anyone is here looking for a quick hacky fix just wrap your this.$router.currentRoute in a setTimeout.

Hi, I think this is kind of related so I’ll ask it here.

Version: vue-router 3.0.6

What’s happening
I have this route definition

{
  path: '/',
  component: SomeLayoutComponent1,
  children: [
    {
      path: '',
      name: 'home'
    }
  ]
},
{
  path: '/',
  component: SomeLayoutComponent2,
  children: [
    {
      path: 'a',
      name: 'a'
    }
  ],
},
{
  path: '/',
  component: SomeLayoutComponent3,
  children: [
    {
      path: 'b',
      name: 'b',
      children: [
        {
          path: 'c',
          name: 'c'
        }
      ]
    }
  ]
}

in my App.vue in the mounted hook i have

mounted () {
  let currentPath = this.$route.path
}

problem is, when I refresh or enter url to /b/c
the currentPath is showing ‘/’

do you guys have any idea given my route setup and the vue mounted hook?

For now I used window.location.pathname to get current path at this point

1 Like

Hi everyone I think I had a similar problem.
when I tried to watch the this.$route object to show or hide a header nav component (single file component) on the /login path.

I did something that solved my problem for now:
I stopped to watch this.$route and instead of, put it on a computed method called getRoutePath (you can name it whataever you want). my code on App.vue is like this:

<template>
  <div id="app" class="container-scroller">
    <TheHeader v-if="this.getRoutePath && this.getRoutePath !== '/login'" />

    <div class="container-fluid page-body-wrapper">
      <router-view></router-view>
    </div>

    <TheFooter v-if="this.getRoutePath && this.getRoutePath !== '/login'" />
  </div>
</template>

<script>
import TheHeader from "@/components/TheHeader";
import TheFooter from "@/components/TheFooter";

export default {
  name: "App",
  components: {
    TheHeader,
    TheFooter
  },
  computed: {
    getRoutePath() {
      //console.log(this.$route.path);
      return this.$route.path;
    }
  }
};
</script>