vuejs / router

🚦 The official router for Vue.js
https://router.vuejs.org/
MIT License
3.93k stars 1.19k forks source link

Force reload when clicking on a router-link even if the route doesn't change #1257

Closed roonie007 closed 2 years ago

roonie007 commented 2 years ago

Version

4.0.12

Reproduction link

codesandbox.io

Steps to reproduce

Simply add a router-link to a view that points to the current page. Then click on that link multiple times, the page or the router-view does not reload or refresh

What is expected?

Refresh or reload the router-view ( same as any HTML a tag )

What is actually happening?

Nothing

posva commented 2 years ago

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

posva commented 2 years ago

Note you can pass a random query parameter to force a navigation:

router.push({ query: { _r: Date.now() }})
roonie007 commented 2 years ago

Note you can pass a random query parameter to force a navigation:

router.push({ query: { _r: Date.now() }})

Already tried that, but it's a hack and it will just pollute the url, I am looking for a cleaner solution.


Duplicate of vuejs/vue-router#974

I am not sure that's a duplication since it's on a different repo and it's not the same version, more than that it's opened since 2016, i think it's completely ignored by Vue core team.

roonie007 commented 2 years ago

An undocumented argument called force can be used to force refresh the page, regardless of whether the URL or path is the same as the current location.

https://github.com/vuejs/router/blob/87d09f81ba11a9153af45836f4b10a5736257cae/src/types/index.ts#L78-L81

@posva Perhaps it's worth adding to the documentation?

posva commented 2 years ago

It's not documented for a reason πŸ˜‰ . Don't rely on it as it could be removed in the future.

roonie007 commented 2 years ago

Let's hope not πŸ˜„

messenjer commented 2 years ago

What do you think about doing this?

await router.push({ path: '/' })
router.go(0)

if delta equals 0, it has the same result as calling location.reload() https://developer.mozilla.org/en-US/docs/Web/API/History/go

inca commented 2 years ago

Was annoyed by a slightly different use case: fetching data in mounted which wasn't called if you navigate within the same route with different params (e.g. from /user/joe to /user/jane).

Official documentation suggests a pattern of having to watch params in addition to mount, which is a bit too repetitive for my taste, so I finally ended up with <RouterView :key="$route.url"/> which does the job.

If you don't rely on params and different URLs, you can create a global variable, assign a random value in one of the navigation guards, and use that as a key for router view. This way URL doesn't get polluted, and the components are correctly mounted/unmounted on every navigation. You can also customize this behaviour by placing the key on different RouterView levels (in case you have hierarchies or nesting) or by scoping the navigation guards.

drennvinn commented 1 year ago

many thanks @inca πŸ™ I was pulling my hair out with the same problem and I didn't think about your solution. this totally solves my problem

posva commented 1 year ago

BTW, you don’t need the repetition: using one watch with immediate true is enough (no key on router view) This is more performant than using a key

inca commented 1 year ago

Interesting. So, what I'm about to say is probably very controversial in Vue community (and kind of off topic), so sorry in advance. IMO/YMMV implied everywhere.

But here goes:

  1. I tend to stay away from watch, and not because of performance problems (it's as performant as other Vue's reactive stuff like computed props and template expressions), but because it breaks the declarativeness of the view layer of the application. I.e. instead of thinking "here's my data, I expect it (and all its deps) to be rendered" you start thinking "when data changes, do something" which is, in my opinion, a slippery path to causality issues and ultimately a lot of 🍝 and debugging. (In fact I've just checked, I've built very big and very dynamic applications and managed to not use watch not even once, and this is something I recommend to not do to the teammates as well.)

  2. I don't really care that much about DOM reusing on router changes. Unless you're doing something really DOM-heavy, you (and, more importantly, your users) won't notice a performance impact of remounting anyway. In this case it also comes at the expense of DX (because as mentioned above, I don't really like thinking in watch terms). The mere fact that Vue Router doesn't have an option to key the RouterView by the props it injects makes me really sad (and occasionally want to build a plugin or similar) β€” because keying them manually is also tedious and somewhat error prone if you have complex route hierarchy.

I guess the justification for my case is that I only have 3 level nesting, and re-mounting matters only on the last level. In my head even though it's a hack it's still applied only on a couple of layouts (as opposed to every route component) and therefore is much better localized than watch (which is also a hack to be frank).

carbdias commented 8 months ago

How to do this and keep query parameters?

edmidev commented 6 months ago

The simplest solution is to add a :key attribute to :

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

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

<script> import { useRoute } from 'vue-router' const route = useRoute() </script>

This is because Vue Router does not notice any change if the same component is being addressed. With the key, any change to the path will trigger a reload of the component with the new data.

gabrieldasilva commented 2 months ago

The simplest solution is to add a :key attribute to :

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

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

<script> import { useRoute } from 'vue-router' const route = useRoute() </script>

This is because Vue Router does not notice any change if the same component is being addressed. With the key, any change to the path will trigger a reload of the component with the new data.

The solutions from @edmidev seems to work fine. I was trying to use the "force" parameter from the docs like this:

<RouterLink :to={ path: url, force:true }></RouterLink>

But is not working (at least for me or my setup). Also, I tested the watcher way and seems to work too.

Just for a bit of context.

In my case, I have different routes for pages that build up dynamically and share the same DefaultView (kinda similar to a page builder). So, when the page loads, I read my page builder config three and load all the content block components.

I'm using v4.3.3 of the router.

It kinda feel that the right way is to force the reload like when reloading a page but I will stick to this workaround for now.