Skip to content

Commit 402dc41

Browse files
authored
[Blazor] Render Blazor Webassembly components from MVC (#25203)
* Server pieces * Changes for prerendering * Discover client components * tmp * Cleanup * Cleanups * Undo changes * Remove unwanted changes * Move interop class to its own file * Cleanup unwanted changes * Add test rendering multiple client-side components * Unit tests and E2E tests * Cleanups * Addressed feedback * Rename Client to WebAssembly in RenderMode * Update generated js files * Cleaned up JS and addressed feedback * Client->WebAssembly and other feedback * Unify component discovery code and use webassembly instead of 'client' * Update js files * Fix tests
1 parent 78a587b commit 402dc41

File tree

42 files changed

+1301
-278
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1301
-278
lines changed

src/Components/Server/src/Circuits/ServerComponentDeserializer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,13 @@ internal class ServerComponentDeserializer
5959
{
6060
private readonly IDataProtector _dataProtector;
6161
private readonly ILogger<ServerComponentDeserializer> _logger;
62-
private readonly ServerComponentTypeCache _rootComponentTypeCache;
62+
private readonly RootComponentTypeCache _rootComponentTypeCache;
6363
private readonly ComponentParameterDeserializer _parametersDeserializer;
6464

6565
public ServerComponentDeserializer(
6666
IDataProtectionProvider dataProtectionProvider,
6767
ILogger<ServerComponentDeserializer> logger,
68-
ServerComponentTypeCache rootComponentTypeCache,
68+
RootComponentTypeCache rootComponentTypeCache,
6969
ComponentParameterDeserializer parametersDeserializer)
7070
{
7171
// When we protect the data we use a time-limited data protector with the

src/Components/Server/src/DependencyInjection/ComponentServiceCollectionExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ public static IServerSideBlazorBuilder AddServerSideBlazor(this IServiceCollecti
5757
services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<StaticFileOptions>, ConfigureStaticFilesOptions>());
5858
services.TryAddSingleton<CircuitFactory>();
5959
services.TryAddSingleton<ServerComponentDeserializer>();
60-
services.TryAddSingleton<ServerComponentTypeCache>();
60+
services.TryAddSingleton<RootComponentTypeCache>();
6161
services.TryAddSingleton<ComponentParameterDeserializer>();
6262
services.TryAddSingleton<ComponentParametersTypeCache>();
6363
services.TryAddSingleton<CircuitIdFactory>();

src/Components/Server/src/Microsoft.AspNetCore.Components.Server.csproj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22
<PropertyGroup>
33
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
44
<Description>Runtime server features for ASP.NET Core Components.</Description>
@@ -54,6 +54,8 @@
5454
<Compile Include="$(ComponentsSharedSourceRoot)src\CacheHeaderSettings.cs" Link="Shared\CacheHeaderSettings.cs" />
5555
<Compile Include="$(ComponentsSharedSourceRoot)src\ArrayBuilder.cs" LinkBase="Circuits" />
5656
<Compile Include="$(ComponentsSharedSourceRoot)src\ElementReferenceJsonConverter.cs" />
57+
<Compile Include="$(ComponentsSharedSourceRoot)src\ComponentParametersTypeCache.cs" />
58+
<Compile Include="$(ComponentsSharedSourceRoot)src\RootComponentTypeCache.cs" />
5759

5860
<Compile Include="..\..\Shared\src\BrowserNavigationManagerInterop.cs" />
5961
<Compile Include="..\..\Shared\src\JsonSerializerOptionsProvider.cs" />

src/Components/Server/test/Circuits/ServerComponentDeserializerTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ private ServerComponentDeserializer CreateServerComponentDeserializer()
320320
return new ServerComponentDeserializer(
321321
_ephemeralDataProtectionProvider,
322322
NullLogger<ServerComponentDeserializer>.Instance,
323-
new ServerComponentTypeCache(),
323+
new RootComponentTypeCache(),
324324
new ComponentParameterDeserializer(NullLogger<ComponentParameterDeserializer>.Instance, new ComponentParametersTypeCache()));
325325
}
326326

src/Components/Server/src/Circuits/ServerComponentTypeCache.cs renamed to src/Components/Shared/src/RootComponentTypeCache.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
namespace Microsoft.AspNetCore.Components
1010
{
1111
// A cache for root component types
12-
internal class ServerComponentTypeCache
12+
internal class RootComponentTypeCache
1313
{
1414
private readonly ConcurrentDictionary<Key, Type> _typeToKeyLookUp = new ConcurrentDictionary<Key, Type>();
1515

@@ -39,14 +39,14 @@ private static Type ResolveType(Key key, Assembly[] assemblies)
3939
return assembly.GetType(key.Type, throwOnError: false, ignoreCase: false);
4040
}
4141

42-
private struct Key : IEquatable<Key>
42+
private readonly struct Key : IEquatable<Key>
4343
{
4444
public Key(string assembly, string type) =>
4545
(Assembly, Type) = (assembly, type);
4646

47-
public string Assembly { get; set; }
47+
public string Assembly { get; }
4848

49-
public string Type { get; set; }
49+
public string Type { get; }
5050

5151
public override bool Equals(object obj) => Equals((Key)obj);
5252

src/Components/Web.JS/dist/Release/blazor.server.js

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Components/Web.JS/dist/Release/blazor.webassembly.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/Components/Web.JS/src/Boot.Server.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ import { shouldAutoStart } from './BootCommon';
77
import { RenderQueue } from './Platform/Circuits/RenderQueue';
88
import { ConsoleLogger } from './Platform/Logging/Loggers';
99
import { LogLevel, Logger } from './Platform/Logging/Logger';
10-
import { discoverComponents, CircuitDescriptor } from './Platform/Circuits/CircuitManager';
10+
import { CircuitDescriptor } from './Platform/Circuits/CircuitManager';
1111
import { setEventDispatcher } from './Rendering/RendererEventDispatcher';
1212
import { resolveOptions, CircuitStartOptions } from './Platform/Circuits/CircuitStartOptions';
1313
import { DefaultReconnectionHandler } from './Platform/Circuits/DefaultReconnectionHandler';
1414
import { attachRootComponentToLogicalElement } from './Rendering/Renderer';
15+
import { discoverComponents, ServerComponentDescriptor } from './Services/ComponentDescriptorDiscovery';
1516

1617
let renderingFailed = false;
1718
let started = false;
@@ -29,7 +30,7 @@ async function boot(userOptions?: Partial<CircuitStartOptions>): Promise<void> {
2930
options.reconnectionHandler = options.reconnectionHandler || window['Blazor'].defaultReconnectionHandler;
3031
logger.log(LogLevel.Information, 'Starting up blazor server-side application.');
3132

32-
const components = discoverComponents(document);
33+
const components = discoverComponents(document, 'server') as ServerComponentDescriptor[];
3334
const circuit = new CircuitDescriptor(components);
3435

3536
const initialConnection = await initializeConnection(options, logger, circuit);

src/Components/Web.JS/src/Boot.WebAssembly.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { DotNet } from '@microsoft/dotnet-js-interop';
22
import './GlobalExports';
33
import * as Environment from './Environment';
44
import { monoPlatform } from './Platform/Mono/MonoPlatform';
5-
import { renderBatch, getRendererer } from './Rendering/Renderer';
5+
import { renderBatch, getRendererer, attachRootComponentToElement, attachRootComponentToLogicalElement } from './Rendering/Renderer';
66
import { SharedMemoryRenderBatch } from './Rendering/RenderBatch/SharedMemoryRenderBatch';
77
import { shouldAutoStart } from './BootCommon';
88
import { setEventDispatcher } from './Rendering/RendererEventDispatcher';
@@ -11,6 +11,8 @@ import { WebAssemblyConfigLoader } from './Platform/WebAssemblyConfigLoader';
1111
import { BootConfigResult } from './Platform/BootConfig';
1212
import { Pointer } from './Platform/Platform';
1313
import { WebAssemblyStartOptions } from './Platform/WebAssemblyStartOptions';
14+
import { WebAssemblyComponentAttacher } from './Platform/WebAssemblyComponentAttacher';
15+
import { discoverComponents, WebAssemblyComponentDescriptor } from './Services/ComponentDescriptorDiscovery';
1416

1517
let started = false;
1618

@@ -71,8 +73,31 @@ async function boot(options?: Partial<WebAssemblyStartOptions>): Promise<void> {
7173
const environment = options?.environment;
7274

7375
// Fetch the resources and prepare the Mono runtime
74-
const bootConfigResult = await BootConfigResult.initAsync(environment);
76+
const bootConfigPromise = BootConfigResult.initAsync(environment);
77+
78+
// Leverage the time while we are loading boot.config.json from the network to discover any potentially registered component on
79+
// the document.
80+
const discoveredComponents = discoverComponents(document, 'webassembly') as WebAssemblyComponentDescriptor[];
81+
const componentAttacher = new WebAssemblyComponentAttacher(discoveredComponents);
82+
window['Blazor']._internal.registeredComponents = {
83+
getRegisteredComponentsCount: () => componentAttacher.getCount(),
84+
getId: (index) => componentAttacher.getId(index),
85+
getAssembly: (id) => BINDING.js_string_to_mono_string(componentAttacher.getAssembly(id)),
86+
getTypeName: (id) => BINDING.js_string_to_mono_string(componentAttacher.getTypeName(id)),
87+
getParameterDefinitions: (id) => BINDING.js_string_to_mono_string(componentAttacher.getParameterDefinitions(id) || ''),
88+
getParameterValues: (id) => BINDING.js_string_to_mono_string(componentAttacher.getParameterValues(id) || ''),
89+
};
90+
91+
window['Blazor']._internal.attachRootComponentToElement = (selector, componentId, rendererId) => {
92+
const element = componentAttacher.resolveRegisteredElement(selector);
93+
if (!element) {
94+
attachRootComponentToElement(selector, componentId, rendererId);
95+
} else {
96+
attachRootComponentToLogicalElement(rendererId, element, componentId);
97+
}
98+
};
7599

100+
const bootConfigResult = await bootConfigPromise;
76101
const [resourceLoader] = await Promise.all([
77102
WebAssemblyResourceLoader.initAsync(bootConfigResult.bootConfig, options || {}),
78103
WebAssemblyConfigLoader.initAsync(bootConfigResult)]);

0 commit comments

Comments
 (0)