Scenario and issues
If we have internal page data to update, we can implement it by calling component methods to update when the event is triggered, other than to do a whole page re-render. However, we need to call $router.push() method to navigate to a destionation page from another one, and this is my case:
- I make a component(a button) which is used in different pages to navigate to page A, the component calls
$router.push()to jump to page A , - Meanwhile, this component is also used iniside page A, that means it's internal route jump to update a part of content in page A;
I use the named route with params binding the changed data page A needs in $router.push(). By this way, page A can always get data from the named route and doesn't need to take care of these two different types of navigation. But the issue comes:
- I cannot do an internal update of page A when the I trigger the component in page A;
- To watch
$routeor usebeforeRouteUpdatehook to capture changes in page A is not working. The route navigation doesn't trigger these functions, even when the passed params are changed inpush().
More details about $route.push() method
After trying some ways I fixed this issue and found more details about push() method, which may not be clearly elaborated in the official docs.
-
push()methods returns a promise, so we can catch the error to get reasons when it fails. By addingtry catch, I got this: Error: Avoided redundant navigation to current location: "xxxx"

-
From the error message above, I see that redundant navigation is not allowed to forward in current vue-router version. I tried to add duplicateNavigationPolicy: "reload" when creating the router instance, and it didn't work as well. As vue discourages router.push if target path matches current path, see: https://github.com/ets-berkeley-edu/boac/pull/1959, this is the reason why '$route' cannot be watched, as the navigation is not triggered at all. The '$route' doesn't get the pushed parameters.
-
Redundant navigation is not allowed. This is understandable as it will reduce page render cost, there is no need to re-render the same page when only parts of data is changed, Vue router suggests use
watch $routeor callbeforeRouteUpdate()hook to monitor route params changes. But the prerequisite to get new state in $route is that: the route params are passed into $route. Since the redundant/same route/current path destination route will not be proceeded by default, the params won't be passed into $route, the hooks cannot be triggered, thus the watcher won't see any changes. The official doc doesn't address this very clearly.
Ways to get through
So, to make sure the redundant route's parameters is passed to $route before the route is considered to be abandoned(not proceeded) is the key to fix this issue. There are following ways to do this. they are all related to how to use push() method.
-
Don't use the
named routeto push, instead, use thepath routewithqueryto pass data. As theparamswon't be passed if it is a redundant route to$routewhen using thenamed route:// avoid using named route with params router.push({ name: 'user', params: { userId: '123' } }); // use this way: path and query router.push({ path: 'user', query: { userId: '123' } }); -
However, we may need the
named routeto hide the passed parameters detailsin some cases, while usingpath routewith query doesn't hide the query info in the URL address. A compromise way is to usenamed routewithpathandquery, we put some insensitive data into query(one key is enough, but keep it changed when callpush. The route will not be recognized as the same redundant one as we have a different query key.
Misleading examples from official docs
What the official doc emphasizes that:
paramsare ignored if a path is provided, which is not the case for query.
And the provided examples tell:
- when using
params, we should provide thename; - when providing
path,paramswill be ignored,querywill not be ignored;
This really misled me, I thought that:
pathandquerywill overridenameandparams, so ifpathwithqueryare used,paramswill be ignored as well
However, the above understanding is not true.
Solution
After I tried different group of the fourkeywords(name+params, path+query) , I found a way out. We can combine named and path route together, meanwhile, by passing both query and params, the params will not be ignored. Then, the final solution for my case is the following:
router.push({
name: 'user',
params: { userId: '123', .........., serveral key-values },
path: '/user',
query: {key: 'not-sensitive'}
});
By doing this, params data can be passed to $route and watched, and internal page route can be done.
In fact, this is not exactly the same route as query is used here. However, if we have to use
paramsin our project, this is a compromise solution.
Comments