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
$route
or usebeforeRouteUpdate
hook 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 $route
or 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 route
to push, instead, use thepath route
withquery
to pass data. As theparams
won't be passed if it is a redundant route to$route
when 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 route
to hide the passed parameters detailsin some cases, while usingpath route
with query doesn't hide the query info in the URL address. A compromise way is to usenamed route
withpath
andquery
, 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:
params
are 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
,params
will be ignored,query
will not be ignored;
This really misled me, I thought that:
path
andquery
will overridename
andparams
, so ifpath
withquery
are used,params
will 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
params
in our project, this is a compromise solution.
Comments