1
1
import { synchronizeDomContent } from '../Rendering/DomMerging/DomSync' ;
2
- import { isNavigationInterceptionEnabled } from './NavigationManager' ;
3
- import { handleClickForNavigationInterception } from './NavigationUtils' ;
2
+ import { handleClickForNavigationInterception , hasInteractiveRouter } from './NavigationUtils' ;
4
3
5
4
/*
6
- We want this to work independently from all the existing logic for NavigationManager and EventDelegator, since
7
- this feature should be available in minimal .js bundles that don't include support for any interactive mode.
8
- So, this file should not import those modules. Likewise, we don't want NavigationManager.ts to import this module,
9
- since then it would have to bundle all the DOM-diffing logic in blazor.server|webassembly|webview.js.
10
-
11
- However, when NavigationManager/EventDelegator are being used, we do have to defer to them since SPA-style
12
- interactive navigation needs to take precedence. The approach is:
13
- - When blazor.web.js starts up, it will enable progressively-enhanced (PE) nav
14
- - If an interactive <Router/> is added, it will call enableNavigationInterception and that will disable PE nav's event listener.
15
- - When NavigationManager.ts sees a navigation occur, it goes through a complex flow (respecting @preventDefault,
16
- triggering OnLocationChanging, evaluating the URL against the .NET route table, etc). Normally this will conclude
17
- by picking a component page and rendering it without notifying the PE nav logic at all.
18
- - But if no component page is matched, it then explicitly calls back into the PE nav logic to fall back on the logic
19
- that would have happened if there was no interactive <Router/>. As such, PE nav isn't *really* disabled; it just only
20
- runs as a fallback if <Router/> nav doesn't match the URL.
21
- - If an interactive <Router/> is removed, we don't currently handle that. No notification goes back from .NET
22
- to JS about that. But if the scenario becomes important, we could add some disableNavigationInterception and resume PE nav.
5
+ In effect, we have two separate client-side navigation mechanisms:
6
+
7
+ [1] Interactive client-side routing. This is the traditional Blazor Server/WebAssembly navigation mechanism for SPAs.
8
+ It is enabled whenever you have a <Router/> rendering as interactive. This intercepts all navigation within the
9
+ base href URI space and tries to display a corresponding [Route] component or the NotFoundContent.
10
+ [2] Progressively-enhanced navigation. This is a new mechanism in .NET 8 and is only relevant for multi-page apps.
11
+ It is enabled when you load blazor.web.js and don't have an interactive <Router/>. This intercepts navigation within
12
+ the base href URI space and tries to load it via a `fetch` request and DOM syncing.
13
+
14
+ Only one of these can be enabled at a time, otherwise both would be trying to intercept click/popstate and act on them.
15
+ In fact even if we made the event handlers able to coexist, the two together would still not produce useful behaviors because
16
+ [1] implies you have a <Router/>, and that will try to supply UI content for all pages or NotFoundContent if the URL doesn't
17
+ match a [Route] component, so there would be nothing left for [2] to handle.
18
+
19
+ So, whenever [1] is enabled, we automatically disable [2].
20
+
21
+ However, a single site can use both [1] and [2] on different URLs.
22
+ - You can navigate from [1] to [2] by setting up the interactive <Router/> not to know about any [Route] components in your MPA,
23
+ and so it will fall back on a full-page load to get from the SPA URLs to the MPA URLs.
24
+ - You can navigate from [2] to [1] in that it just works by default. A <Router/> can be added dynamically and will then take
25
+ over and disable [2].
26
+
27
+ Note that we don't reference NavigationManager.ts from NavigationEnhancement.ts or vice-versa. This is to ensure we could produce
28
+ different bundles that only contain minimal content.
23
29
*/
24
30
25
31
let currentEnhancedNavigationAbortController : AbortController | null ;
@@ -30,7 +36,7 @@ export function attachProgressivelyEnhancedNavigationListener() {
30
36
}
31
37
32
38
function onBodyClicked ( event : MouseEvent ) {
33
- if ( isNavigationInterceptionEnabled ( ) ) {
39
+ if ( hasInteractiveRouter ( ) ) {
34
40
return ;
35
41
}
36
42
@@ -41,7 +47,7 @@ function onBodyClicked(event: MouseEvent) {
41
47
}
42
48
43
49
function onPopState ( state : PopStateEvent ) {
44
- if ( isNavigationInterceptionEnabled ( ) ) {
50
+ if ( hasInteractiveRouter ( ) ) {
45
51
return ;
46
52
}
47
53
0 commit comments