single-spa / single-spa-vue

a single-spa plugin for vue.js applications
MIT License
182 stars 42 forks source link

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

Closed asafn-jfrog closed 6 months ago

asafn-jfrog commented 6 months ago

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 https://github.com/single-spa/single-spa-vue/issues/85

AckermannJan commented 6 months ago

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.

asafn-jfrog commented 6 months ago

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?

asafn-jfrog commented 6 months ago

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.

AckermannJan commented 6 months ago

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.

asafn-jfrog commented 6 months ago

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