Vue-router same page/redundant route navigation and usage of $router.push

2020年08月15日 6009Browse 1Like 0Comments

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 use beforeRouteUpdate 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 in push().

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.

  1. push() methods returns a promise, so we can catch the error to get reasons when it fails. By adding try catch, I got this: Error: Avoided redundant navigation to current location: "xxxx"

  2. 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.

  3. 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 call beforeRouteUpdate() 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 the path route with query to pass data. As the params won't be passed if it is a redundant route to $route when using the named 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 using path route with query doesn't hide the query info in the URL address. A compromise way is to use named route with path and query, we put some insensitive data into query(one key is enough, but keep it changed when call push. 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 the name;
  • when providing path, params will be ignored, query will not be ignored;

This really misled me, I thought that:

  • path and query will override name and params, so if path with query 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.

Reference

  1. https://router.vuejs.org/guide/essentials/navigation.html
  2. https://router.vuejs.org/guide/essentials/dynamic-matching.html#reacting-to-params-changes
  3. https://www.sinocalife.com/use-sessionstorage-to-fix-page-blank-issue-due-to-vue-route-push-params-loss

Sunflower

Stay hungry stay foolish

Comments