Skip to content

Commit 11d458e

Browse files
author
Mackinnon Buck
committed
Merged PR 36408: [release/8.0] [Blazor] Auto render mode improvements
# Auto render mode improvements Backport of dotnet/aspnetcore#53159 Improves the Auto render mode so that components are more responsive and have a decreased initial time to interactivity when WebAssembly resources are not already cached. ## Description One of the goals of the Auto render mode was to allow apps to become interactive as quickly as possible via Server interactivity, while WebAssembly bits were downloaded in the background for use on future visits to the site. However, since WebAssembly resources were being downloaded with maximal parallelism, the quality of the websocket connection required for Server interactivity was negatively impacted, often to the extent that the websocket wouldn't connect until WebAssembly resources had finished downloading completely, largely defeating the purpose of the Auto render mode. This PR makes the following improvements: * Removes a problematic timeout on loading the WebAssembly boot config. This fixes a problem where Server interactivity was always being used when the boot config took too long to load. * Introduces a limit to the maximum parallel WebAssembly resource downloads when an Auto component initiates the startup of the WebAssembly runtime. This limit is set to 1 and overrides any user-specified limit. * Fixes an issue where the circuit sometimes remains open even if WebAssembly gets selected for Auto interactivity. I provided a preview of these changes in dotnet/aspnetcore#52154 (comment) so that customers could try them out, and the feedback so far has been very positive. Fixes dotnet/aspnetcore#52154 ## Customer Impact A significant number of customers reported being affected by this problem in issues like dotnet/aspnetcore#52154. I supplied customers with a preview of the fix that they could patch it into their app, and many indicated that their problems were resolved by the fix. The Auto render mode was one of the key features released in .NET 8, so it's important that it works in the way we've been advertising.   ## Regression? - [ ] Yes - [X] No ## Risk - [ ] High - [ ] Medium - [X] Low The core Auto render mode functionality is unaffected by this change - we added small tweaks to adjust the throttling amount and remove a problematic timeout. Additional tests were added to verify the changes in behavior, and we've been testing these changes manually to ensure they work well in real-world scenarios (various device types and connection qualities). ## Verification - [X] Manual (required) - [X] Automated ## Packaging changes reviewed? - [ ] Yes - [ ] No - [X] N/A
1 parent 4caae77 commit 11d458e

4 files changed

Lines changed: 19 additions & 20 deletions

File tree

dist/Release/blazor.web.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Boot.Web.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ function boot(options?: Partial<WebStartOptions>) : Promise<void> {
3737
started = true;
3838
options = options || {};
3939
options.logLevel ??= LogLevel.Error;
40-
Blazor._internal.loadWebAssemblyQuicklyTimeout = 3000;
4140
Blazor._internal.isBlazorWeb = true;
4241

4342
// Defined here to avoid inadvertently imported enhanced navigation

src/GlobalExports.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ export interface IBlazor {
7979
receiveWebAssemblyDotNetDataStream?: (streamId: number, data: any, bytesRead: number, errorMessage: string) => void;
8080
receiveWebViewDotNetDataStream?: (streamId: number, data: any, bytesRead: number, errorMessage: string) => void;
8181
attachWebRendererInterop?: typeof attachWebRendererInterop;
82-
loadWebAssemblyQuicklyTimeout?: number;
8382
isBlazorWeb?: boolean;
8483

8584
// JSExport APIs

src/Services/WebRootComponentManager.ts

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { disposeCircuit, hasStartedServer, isCircuitAvailable, startCircuit, sta
99
import { hasLoadedWebAssemblyPlatform, hasStartedLoadingWebAssemblyPlatform, hasStartedWebAssembly, isFirstUpdate, loadWebAssemblyPlatformIfNotStarted, resolveInitialUpdate, setWaitForRootComponents, startWebAssembly, updateWebAssemblyRootComponents, waitForBootConfigLoaded } from '../Boot.WebAssembly.Common';
1010
import { MonoConfig } from 'dotnet';
1111
import { RootComponentManager } from './RootComponentManager';
12-
import { Blazor } from '../GlobalExports';
1312
import { getRendererer } from '../Rendering/Renderer';
1413
import { isPageLoading } from './NavigationEnhancement';
1514
import { setShouldPreserveContentOnInteractiveComponentDisposal } from '../Rendering/BrowserRenderer';
@@ -100,12 +99,18 @@ export class WebRootComponentManager implements DescriptorHandler, RootComponent
10099
return;
101100
}
102101

103-
if (descriptor.type === 'auto' || descriptor.type === 'webassembly') {
104-
// Eagerly start loading the WebAssembly runtime, even though we're not
105-
// activating the component yet. This is becuase WebAssembly resources
106-
// may take a long time to load, so starting to load them now potentially reduces
107-
// the time to interactvity.
102+
// When encountering a component with a WebAssembly or Auto render mode,
103+
// start loading the WebAssembly runtime, even though we're not
104+
// activating the component yet. This is becuase WebAssembly resources
105+
// may take a long time to load, so starting to load them now potentially reduces
106+
// the time to interactvity.
107+
if (descriptor.type === 'webassembly') {
108108
this.startLoadingWebAssemblyIfNotStarted();
109+
} else if (descriptor.type === 'auto') {
110+
// If the WebAssembly runtime starts downloading because an Auto component was added to
111+
// the page, we limit the maximum number of parallel WebAssembly resource downloads to 1
112+
// so that the performance of any Blazor Server circuit is minimally impacted.
113+
this.startLoadingWebAssemblyIfNotStarted(/* maxParallelDownloadsOverride */ 1);
109114
}
110115

111116
const ssrComponentId = this._nextSsrComponentId++;
@@ -120,26 +125,20 @@ export class WebRootComponentManager implements DescriptorHandler, RootComponent
120125
this.circuitMayHaveNoRootComponents();
121126
}
122127

123-
private async startLoadingWebAssemblyIfNotStarted() {
128+
private async startLoadingWebAssemblyIfNotStarted(maxParallelDownloadsOverride?: number) {
124129
if (hasStartedLoadingWebAssemblyPlatform()) {
125130
return;
126131
}
127132

128133
setWaitForRootComponents();
129134

130135
const loadWebAssemblyPromise = loadWebAssemblyPlatformIfNotStarted();
131-
132-
// If WebAssembly resources can't be loaded within some time limit,
133-
// we take note of this fact so that "auto" components fall back
134-
// to using Blazor Server.
135-
setTimeout(() => {
136-
if (!hasLoadedWebAssemblyPlatform()) {
137-
this.onWebAssemblyFailedToLoadQuickly();
138-
}
139-
}, Blazor._internal.loadWebAssemblyQuicklyTimeout);
140-
141136
const bootConfig = await waitForBootConfigLoaded();
142137

138+
if (maxParallelDownloadsOverride !== undefined) {
139+
bootConfig.maxParallelDownloads = maxParallelDownloadsOverride;
140+
}
141+
143142
if (!areWebAssemblyResourcesLikelyCached(bootConfig)) {
144143
// Since WebAssembly resources aren't likely cached,
145144
// they will probably need to be fetched over the network.
@@ -299,6 +298,8 @@ export class WebRootComponentManager implements DescriptorHandler, RootComponent
299298
this.updateWebAssemblyRootComponents(batchJson);
300299
}
301300
}
301+
302+
this.circuitMayHaveNoRootComponents();
302303
}
303304

304305
private updateWebAssemblyRootComponents(operationsJson: string) {

0 commit comments

Comments
 (0)