Vue-router: Is there any way to force navigation when clicking on a router-link even if the route doesn't change?

Created on 13 Oct 2018  路  19Comments  路  Source: vuejs/vue-router

What problem does this feature solve?

Reload current page's state or refresh the page.

For example, I have a <HeaderBar> which contains a Home button, just like the Facebook one.
If I'm in another page (rather than Home), when I hit this button, it will navigate to Home. But when I'm in Home page, hit this button it does nothing.

It's expected to reload the page, and scroll to the top, right?

Please don't tell me to write out an action to refresh the state and scroll to top. This header bar is a shared component, and the Home button can be hit at any where.

What does the proposed API look like?

Some thing like

<router-link :to="{ name: 'Home', force: true }">Home</router-link>

or

this.$router.replace({ name: 'Home', force: true });

Most helpful comment

@leoyli

Yeah, I've tried to bind a key to router-view before, but it didn't work.

<router-view :key="$route.fullPath"/>

Since the fullPath have no changes, how does it suppose to reload the page?

Can you give me an example, plz. Talk is cheap, you know.

All 19 comments

You can reload the page, but you should handle the _refresh_ in a click handler if you want to have that custom behaviour

Duplicate of https://github.com/vuejs/vue-router/issues/974

@posva
If so, Navigation Guards (beforeRouteLeave, beforeRouteUpdate) will not be triggered.

How do you deal with that?

yeah, they are not supposed to be triggered as you are in the same route with no param change or whatsoever... You need to refactor the code in methods and call them in both places. Cheers

[Solved]
This is my solution, if any body needs.

I create a base component, and use it instead of <router-link>.
If you hit the link when you're in that page already, it will create a query name _, so the fullPath will be change and the page will be "reload".

You can change the query name to whatever you want, and use the timestamp or some unique string to make the fullPath changes.

<template lang="html">
  <router-link v-bind="$attrs" :to="to" @click.native="clickHandler">
    <slot></slot>
  </router-link>
</template>

<script>
export default {
  name: 'BaseLink',

  props: {
    to: {
      type: [Object, String],
      default: '/',
    },
  },

  data() {
    return {
      currentRoute: null,
    };
  },

  created() {
    this.currentRoute = this.$route.name;
  },

  methods: {
    clickHandler($event) {
      if (!($event.ctrlKey || $event.metaKey) && this.currentRoute === this.to.name) {
        const query = { ...this.$route.query, _: new Date().getTime() };
        const newRoute = { ...this.$route, query };
        this.$router.replace(newRoute);
      }
    },
  },
};
</script>

I doubt this method, it looks dirty and contaminate the history (and generate no real-meaning query in the url). To be clear, you don't want a "real, F5 like" refresh since your script have to be reload and the browser have to reparse it, right? (The OP's solution is in that direction.)

More simple way is to assign an unique key in <router-view /> using one of the properties stored in the params. Manipulate this instead of query would be better.

@leoyli

Yeah, I've tried to bind a key to router-view before, but it didn't work.

<router-view :key="$route.fullPath"/>

Since the fullPath have no changes, how does it suppose to reload the page?

Can you give me an example, plz. Talk is cheap, you know.

You can have something like

<router-view :key="$route.params.theNameYouGive"/>

And you manipulate like:

<router-link :to="{ params: { theNameYouGive: Data.now } }" replace> ... </router-link>

Remember to is passed router.push() and it accept an object also. Doing that, it is more declarative and controllable. I'm using this to decide if the page of component should be rerendered since they will based on id params obtained from URL entry, and my child component can still using nesting <router-view>.

@leoyli
Did you try it in actually?
It doesn't work. The theNameYouGive doesn't make the $route change, beforeRouteUpdate, beforeRouteLeave doesn't triggered and it never refreshes the page.

@trandaison
Sorry my bad. Looks like <router-link> can only change the params when it assigned together with name and this name can not be the same as the current route; or in your route definition you have :param bound with the url.

That is to say in any cases URL have to be change unless you assign key to an internal state (data) and you bind the property name in the key attribute, e.g. <router-view :key="counter">, and use @click="counter++" to update the value in the component.

If you still want to use the approach by changing URL, I suggest you manipulate hash instead of query.

Try: npm i vue-state-router instead of vue-router. Totally same API.
Gives you these feature:

  1. when navigate with push mode, every component will be reload, and scroll back to top
  2. when with replace mode, every component will reuse, and scroll to saved position

I've just ran into this problem also.

Use case:

  • Router setup as /profile/:userId to a component called "Profile"
  • On the profile it loads the data from a api call, however let's say I have a "Friends" tab on the page and I click their profile :to profile with the userId params
  • It won't load because the component is already loaded

Same problem.
Resolved : click link profile event:

() => window.location.href= `/profile/${userId}`

@posva is there a technical limit? I do not see how this would not be the desired default effect.

@cafe4it but doing so your app is not SPA anymore, the whole app will be reload again.

This is my trick to get through this situation:

  • Create a component named <base-link> just likes the <router-link> and use this one instead of <router-link>.
  • If the :to route is different from the current route, render <router-link> in the template.
  • Else, render a <a> tag with :href=this.$router.resolve(this.to).route.fullPath and add an click event handler to redirect to another page named 'Refresh';
  • In the Refresh page, replace the route back to the referer.

Everything works just fine so far.

I have a nice trick. It works with nuxt-link from Nuxt.js, but it can also work with router-kink I believe.

First, you register a click event on your link component
<nuxt-link to="/home" @click.native="onClick" />

After that, you check in the click handler if you are on the same route

if(this.$route.path === '/home') {
    // do some staff here
   // example
   window.scrollTo({ top: 0, behavior: 'smooth' })
}

The condition will be true only when you click the navigation link for the same route

I'm interested in having some sort of functionality here that does this natively.
I want the vue-router to think of the URL like a form-input. So I can render text into it without triggering changes that depend on the URL. At the sametime the URL is a control input.

@posva Can this be re-opened? There seems to be a lot of community support for a option to force a re-render. Please see the use case I shared above, I would love the option to force without the additional complexity of adding a watcher to accomplish something so simple. Thanks in advance.

I added a query param that includes current time as milliseconds and it's working for me.

this.$router.push({ name: 'post', params: { type: 'detail' }, query: { id: 123, time: Date.now() } });
Was this page helpful?
0 / 5 - 0 ratings