single-spa / single-spa-vue

a single-spa plugin for vue.js applications

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Single Spa + Vue3 vue-router triggers route guard multiple times

asafn-jfrog opened this issue · comments

In our Vue.js (Vue3) microfrontend application, we have noticed an issue where the Vue-router route guard is called multiple times when navigating between routes.

setting the 'urlRerouteOnly' flag to true will reduce the calls of the route guard from 3 times to 2 times
using the back and forward buttons in the browser will call the route guard only once, as expected
This behavior is unexpected and could potentially lead to performance issues or unexpected application behavior.

To Reproduce

  1. Clone the repository from https://github.com/asafn-jfrog/single-spa-vue3-route-guard-issue/tree/main
  2. Follow the steps in the README to set up and run the application.
  3. Navigate between routes and observe the console output.
  4. Expected behavior
  5. Route guard is called only once in all navigation scenarios

reconstruction
reconstruction

Additional context
This issue might be related to #85

Hey, just jumping in since we recently had the same issue with vue-router 4. Due to a deliberate choice of the Single-Spa team to include an additional artificial PopStateEvent in the framework, the beforeEach and afterEach callbacks are called twice within the AppShell. This is a known issue for AppShell users but in most cases does not cause any problems.

To prevent the double hook calls in general, there is a public API to achieve it while using history option. The following code snippet demonstrates how to use it:

routerHistory.listening = false;

Alternatively, you can check in your guard if the from -> to route is the same, and if so do nothing in the guard.

Thank you @AckermannJan !
Looks like the final api is router.listening = false/true

This indeed prevents the double route guards invocation, however, now the 'back' and 'forward' browser buttons do not work properly. Any ideas?

Also, blocking the execution on the route guard level like @AckermannJan suggested is still not 100% since with the beforeRouteLeave in-component guard, the 'to' & 'from' are different.

Both things I or rather were suggested in the Vue thread were workarounds. With the browser back and forward button I guess you could write your own event listeners to make it work again.

Regarding checking the to and from, yes this only works for adding the guard not being within the component. This was our solution that we went with.

For anyone out there who is interested... The root cause of this issue, as mentioned above, is the artificial PopStateEvent together with the fact that single-spa is deliberately waiting to trigger the listeners. This causes vue-router to activate the navigation guards a second time, and having the 'to' and 'from' routes the same since the current path has already been changed due to the delay.

Luckily, single-spa provides some additional information on the event, which helped us in the final solution for the issue.

We ended up using the 'singleSpa' flag that is set on the event, and making some changes in vue-router createWebHistory module on the popStateHandler