Skip to content

Latest commit

 

History

History
466 lines (322 loc) · 20.2 KB

File metadata and controls

466 lines (322 loc) · 20.2 KB
title author description monikerRange ms.author ms.custom ms.date uid
ASP.NET Core Blazor startup
guardrex
Learn how to configure Blazor startup.
>= aspnetcore-3.1
riande
mvc
11/08/2022
blazor/fundamentals/startup

ASP.NET Core Blazor startup

[!INCLUDE]

This article explains Blazor app startup configuration.

[!INCLUDE]

For general guidance on ASP.NET Core app configuration for server-side development, see xref:fundamentals/configuration/index.

:::moniker range=">= aspnetcore-8.0"

The Blazor startup process is automatic and asynchronous via the Blazor script (blazor.*.js), where * is either web for a Blazor Web App or webassembly for a Blazor WebAssembly app. The Blazor <script> tag is found in the wwwroot/index.html file of a Blazor WebAssembly app or client of a Blazor Web App, or the App.razor file of a Blazor Web App.

:::moniker-end

:::moniker range=">= aspnetcore-7.0 < aspnetcore-8.0"

The Blazor startup process is automatic and asynchronous via the Blazor script (blazor.*.js), where * is either server for a Blazor Server app or webassembly for a Blazor WebAssembly app. The Blazor <script> tag is found in the wwwroot/index.html file of a Blazor WebAssembly app or the Pages/_Host.cshtml file of a Blazor Server app.

:::moniker-end

:::moniker range=">= aspnetcore-6.0 < aspnetcore-7.0"

The Blazor startup process is automatic and asynchronous via the Blazor script (blazor.*.js), where * is either server for a Blazor Server app or webassembly for a Blazor WebAssembly app. The Blazor <script> tag is found in the wwwroot/index.html file of a Blazor WebAssembly app or the Pages/_Layout.cshtml file of a Blazor Server app.

:::moniker-end

:::moniker range="< aspnetcore-6.0"

The Blazor startup process is automatic and asynchronous via the Blazor script (blazor.*.js), where * is either server for a Blazor Server app or webassembly for a Blazor WebAssembly app. The Blazor <script> tag is found in the wwwroot/index.html file of a Blazor WebAssembly app or the Pages/_Host.cshtml file of a Blazor Server app.

:::moniker-end

To manually start Blazor:

  • Add an autostart="false" attribute and value to the Blazor <script> tag.
  • Place a script that calls Blazor.start() after the Blazor <script> tag and inside the closing </body> tag.
<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
  ...
  Blazor.start();
  ...
</script>

In the preceding example, the {BLAZOR SCRIPT} placeholder is the Blazor script path and file name.

:::moniker range=">= aspnetcore-6.0"

JavaScript initializers

JavaScript (JS) initializers execute logic before and after a Blazor app loads. JS initializers are useful in the following scenarios:

  • Customizing how a Blazor app loads.
  • Initializing libraries before Blazor starts up.
  • Configuring Blazor settings.

JS initializers are detected as part of the build process and imported automatically. Use of JS initializers often removes the need to manually trigger script functions from the app when using Razor class libraries (RCLs).

To define a JS initializer, add a JS module to the project named {NAME}.lib.module.js, where the {NAME} placeholder is the assembly name, library name, or package identifier. Place the file in the project's web root, which is typically the wwwroot folder.

The module exports either or both of the following conventional functions:

  • beforeStart(options, extensions): Called before Blazor starts. For example, beforeStart is used to customize the loading process, logging level, and other options specific to the hosting model.
    • Client-side, beforeStart receives the Blazor options (options in this section's examples) and any extensions (extensions in this section's examples) added during publishing. For example, options can specify the use of a custom boot resource loader.
    • Server-side, beforeStart receives SignalR circuit start options (options in this section's examples).
    • In BlazorWebViews, no options are passed.
  • afterStarted: Called after Blazor is ready to receive calls from JS. For example, afterStarted is used to initialize libraries by making JS interop calls and registering custom elements. The Blazor instance is passed to afterStarted as an argument (blazor in this section's example).

For the file name:

  • If the JS initializers are consumed as a static asset in the project, use the format {ASSEMBLY NAME}.lib.module.js, where the {ASSEMBLY NAME} placeholder is the app's assembly name. For example, name the file BlazorSample.lib.module.js for a project with an assembly name of BlazorSample. Place the file in the app's wwwroot folder.
  • If the JS initializers are consumed from an RCL, use the format {LIBRARY NAME/PACKAGE ID}.lib.module.js, where the {LIBRARY NAME/PACKAGE ID} placeholder is the project's library name or package identifier. For example, name the file RazorClassLibrary1.lib.module.js for an RCL with a package identifier of RazorClassLibrary1. Place the file in the library's wwwroot folder.

The following example demonstrates JS initializers that load custom scripts before and after Blazor has started by appending them to the <head> in beforeStart and afterStarted:

export function beforeStart(options, extensions) {
  var customScript = document.createElement('script');
  customScript.setAttribute('src', 'beforeStartScripts.js');
  document.head.appendChild(customScript);
}

export function afterStarted(blazor) {
  var customScript = document.createElement('script');
  customScript.setAttribute('src', 'afterStartedScripts.js');
  document.head.appendChild(customScript);
}

The preceding beforeStart example only guarantees that the custom script loads before Blazor starts. It doesn't guarantee that awaited promises in the script complete their execution before Blazor starts.

Note

MVC and Razor Pages apps don't automatically load JS initializers. However, developer code can include a script to fetch the app's manifest and trigger the load of the JS initializers.

For an examples of JS initializers, see the following resources:

:::moniker-end

:::moniker range=">= aspnetcore-8.0"

:::moniker-end

:::moniker range=">= aspnetcore-6.0 < aspnetcore-8.0"

:::moniker-end

:::moniker range=">= aspnetcore-6.0"

[!INCLUDE]

Ensure libraries are loaded in a specific order

Append custom scripts to the <head> in beforeStart and afterStarted in the order that they should load.

The following example loads script1.js before script2.js and script3.js before script4.js:

export function beforeStart(options, extensions) {
    var customScript1 = document.createElement('script');
    customScript1.setAttribute('src', 'script1.js');
    document.head.appendChild(customScript1);

    var customScript2 = document.createElement('script');
    customScript2.setAttribute('src', 'script2.js');
    document.head.appendChild(customScript2);
}

export function afterStarted(blazor) {
    var customScript1 = document.createElement('script');
    customScript1.setAttribute('src', 'script3.js');
    document.head.appendChild(customScript1);

    var customScript2 = document.createElement('script');
    customScript2.setAttribute('src', 'script4.js');
    document.head.appendChild(customScript2);
}

Import additional modules

Use top-level import statements in the JS initializers file to import additional modules.

additionalModule.js:

export function logMessage() {
  console.log('logMessage is logging');
}

In the JS initializers file (.lib.module.js):

import { logMessage } from "/additionalModule.js";

export function beforeStart(options, extensions) {
  ...

  logMessage();
}

Import map

Import maps are supported by ASP.NET Core and Blazor.

:::moniker-end

Initialize Blazor when the document is ready

The following example starts Blazor when the document is ready:

<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
  document.addEventListener("DOMContentLoaded", function() {
    Blazor.start();
  });
</script>

In the preceding example, the {BLAZOR SCRIPT} placeholder is the Blazor script path and file name.

Chain to the Promise that results from a manual start

To perform additional tasks, such as JS interop initialization, use then to chain to the Promise that results from a manual Blazor app start:

<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
  Blazor.start().then(function () {
    ...
  });
</script>

In the preceding example, the {BLAZOR SCRIPT} placeholder is the Blazor script path and file name.

:::moniker range=">= aspnetcore-6.0"

Note

For a library to automatically execute additional tasks after Blazor has started, use a JavaScript initializer. Use of a JS initializer doesn't require the consumer of the library to chain JS calls to Blazor's manual start.

:::moniker-end

Load client-side boot resources

When an app loads in the browser, the app downloads boot resources from the server:

  • JavaScript code to bootstrap the app
  • .NET runtime and assemblies
  • Locale specific data

Customize how these boot resources are loaded using the loadBootResource API. The loadBootResource function overrides the built-in boot resource loading mechanism. Use loadBootResource for the following scenarios:

  • Load static resources, such as timezone data or dotnet.wasm, from a CDN.
  • Load compressed assemblies using an HTTP request and decompress them on the client for hosts that don't support fetching compressed contents from the server.
  • Alias resources to a different name by redirecting each fetch request to a new name.

Note

External sources must return the required Cross-Origin Resource Sharing (CORS) headers for browsers to allow cross-origin resource loading. CDNs usually provide the required headers by default.

loadBootResource parameters appear in the following table.

Parameter Description
type The type of the resource. Permissible types include: assembly, pdb, dotnetjs, dotnetwasm, and timezonedata. You only need to specify types for custom behaviors. Types not specified to loadBootResource are loaded by the framework per their default loading behaviors. The dotnetjs boot resource (dotnet.*.js) must either return null for default loading behavior or a URI for the source of the dotnetjs boot resource.
name The name of the resource.
defaultUri The relative or absolute URI of the resource.
integrity The integrity string representing the expected content in the response.

The loadBootResource function can return a URI string to override the loading process. In the following example, the following files from bin/Release/{TARGET FRAMEWORK}/wwwroot/_framework are served from a CDN at https://cdn.example.com/blazorwebassembly/{VERSION}/:

  • dotnet.*.js
  • dotnet.wasm
  • Timezone data

The {TARGET FRAMEWORK} placeholder is the target framework moniker (for example, net7.0). The {VERSION} placeholder is the shared framework version (for example, 7.0.0).

<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
  Blazor.start({
    loadBootResource: function (type, name, defaultUri, integrity) {
      console.log(`Loading: '${type}', '${name}', '${defaultUri}', '${integrity}'`);
      switch (type) {
        case 'dotnetjs':
        case 'dotnetwasm':
        case 'timezonedata':
          return `https://cdn.example.com/blazorwebassembly/{VERSION}/${name}`;
      }
    }
  });
</script>

In the preceding example, the {BLAZOR SCRIPT} placeholder is the Blazor script path and file name.

To customize more than just the URLs for boot resources, the loadBootResource function can call fetch directly and return the result. The following example adds a custom HTTP header to the outbound requests. To retain the default integrity checking behavior, pass through the integrity parameter.

<script src="{BLAZOR SCRIPT}" autostart="false"></script>
<script>
  Blazor.start({
    loadBootResource: function (type, name, defaultUri, integrity) {
      if (type == 'dotnetjs') {
        return null;
      } else {
        return fetch(defaultUri, {
          cache: 'no-cache',
          integrity: integrity,
          headers: { 'Custom-Header': 'Custom Value' }
        });
      }
    }
  });
</script>

In the preceding example, the {BLAZOR SCRIPT} placeholder is the Blazor script path and file name.

When the loadBootResource function returns null, Blazor uses the default loading behavior for the resource. For example, the preceding code returns null for the dotnetjs boot resource (dotnet.*.js) because the dotnetjs boot resource must either return null for default loading behavior or a URI for the source of the dotnetjs boot resource.

The loadBootResource function can also return a Response promise. For an example, see xref:blazor/host-and-deploy/webassembly#compression.

Control headers in C# code

Control headers at startup in C# code using the following approaches.

In the following examples, a Content Security Policy (CSP) is applied to the app via a CSP header. The {POLICY STRING} placeholder is the CSP policy string.

Server-side and prerendered client-side scenarios

Use ASP.NET Core Middleware to control the headers collection.

:::moniker range=">= aspnetcore-6.0"

In the Program file:

:::moniker-end

:::moniker range="< aspnetcore-6.0"

In Startup.Configure of Startup.cs:

:::moniker-end

app.Use(async (context, next) =>
{
    context.Response.Headers.Add("Content-Security-Policy", "{POLICY STRING}");
    await next();
});

The preceding example uses inline middleware, but you can also create a custom middleware class and call the middleware with an extension method in the Program file. For more information, see xref:fundamentals/middleware/write.

Client-side development without prerendering

Pass xref:Microsoft.AspNetCore.Builder.StaticFileOptions to xref:Microsoft.AspNetCore.Builder.StaticFilesEndpointRouteBuilderExtensions.MapFallbackToFile%2A that specifies response headers at the xref:Microsoft.AspNetCore.Builder.StaticFileOptions.OnPrepareResponse stage.

:::moniker range=">= aspnetcore-6.0"

In the server-side Program file:

:::moniker-end

:::moniker range="< aspnetcore-6.0"

In Startup.Configure of Startup.cs:

:::moniker-end

var staticFileOptions = new StaticFileOptions
{
    OnPrepareResponse = context =>
    {
        context.Context.Response.Headers.Add("Content-Security-Policy", 
            "{POLICY STRING}");
    }
};

...

app.MapFallbackToFile("index.html", staticFileOptions);

For more information on CSPs, see xref:blazor/security/content-security-policy.

:::moniker range=">= aspnetcore-7.0"

Client-side loading progress indicators

This section only applies to Blazor WebAssembly apps.

The project template contains Scalable Vector Graphics (SVG) and text indicators that show the loading progress of the app.

The progress indicators are implemented with HTML and CSS using two CSS custom properties (variables) provided by Blazor:

  • --blazor-load-percentage: The percentage of app files loaded.
  • --blazor-load-percentage-text: The percentage of app files loaded, rounded to the nearest whole number.

Using the preceding CSS variables, you can create custom progress indicators that match the styling of your app.

In the following example:

  • resourcesLoaded is an instantaneous count of the resources loaded during app startup.
  • totalResources is the total number of resources to load.
const percentage = resourcesLoaded / totalResources * 100;
document.documentElement.style.setProperty(
  '--blazor-load-percentage', `${percentage}%`);
document.documentElement.style.setProperty(
  '--blazor-load-percentage-text', `"${Math.floor(percentage)}%"`);

The default round progress indicator is implemented in HTML in the wwwroot/index.html file:

<div id="app">
    <svg class="loading-progress">
        <circle r="40%" cx="50%" cy="50%" />
        <circle r="40%" cx="50%" cy="50%" />
    </svg>
    <div class="loading-progress-text"></div>
</div>

To review the project template markup and styling for the default progress indicators, see the ASP.NET Core reference source:

[!INCLUDE]

Instead of using the default round progress indicator, the following example shows how to implement a linear progress indicator.

Add the following styles to wwwroot/css/app.css:

.linear-progress {
    background: silver;
    width: 50vw;
    margin: 20% auto;
    height: 1rem;
    border-radius: 10rem;
    overflow: hidden;
    position: relative;
}

.linear-progress:after {
    content: '';
    position: absolute;
    inset: 0;
    background: blue;
    scale: var(--blazor-load-percentage, 0%) 100%;
    transform-origin: left top;
    transition: scale ease-out 0.5s;
}

A CSS variable (var(...)) is used to pass the value of --blazor-load-percentage to the scale property of a blue pseudo-element that indicates the loading progress of the app's files. As the app loads, --blazor-load-percentage is updated automatically, which dynamically changes the progress indicator's visual representation.

In wwwroot/index.html, remove the default SVG round indicator in <div id="app">...</div> and replace it with the following markup:

<div class="linear-progress"></div>

:::moniker-end

Additional resources

  • Environments: Set the app's environment
  • SignalR (includes sections on SignalR startup configuration)
  • Globalization and localization: Statically set the culture with Blazor.start() (client-side only)
  • Host and deploy: Blazor WebAssembly: Compression