ASP.NET Core Blazor 应用中的 JavaScript 位置

注意

此版本不是本文的最新版本。 有关当前版本,请参阅本文.NET 9 版本。

警告

此版本的 ASP.NET Core 不再受支持。 有关详细信息,请参阅 .NET 和 .NET Core 支持策略。 有关当前版本,请参阅本文.NET 9 版本。

重要

此信息与预发布产品相关,相应产品在商业发布之前可能会进行重大修改。 Microsoft 对此处提供的信息不提供任何明示或暗示的保证。

有关当前版本,请参阅本文.NET 9 版本。

使用以下任一方法加载 JavaScript (JS) 代码:

警告

仅当保证组件采用静态服务器端呈现(静态 SSR)时,才会将 <script> 标记放置在组件文件 (.razor) 中,因为无法动态更新 <script> 标记。

警告

请勿将 <script> 标记置于组件文件 (.razor) 中,因为 <script> 标记无法动态更新。

注意

文档示例通常将脚本置于 <script> 标记中,或从外部文件加载全局脚本。 这些方法使用全局函数污染客户端。 对于生产应用,我们建议将 JS 放入单独的JS模块中,以便在需要时导入。 有关详细信息,请参阅 JavaScript 模块中的 JavaScript 隔离部分。

注意

文档示例将脚本置于 <script> 标记中,或从外部文件加载全局脚本。 这些方法使用全局函数污染客户端。 在 ASP.NET Core 5.0 之前的 Blazor 中,不支持将 JS 放入单独的JS模块中(以供在需要时导入)。 如果应用需要使用 JS 模块来进行 JS 隔离,建议使用 ASP.NET Core 5.0 或更高版本来生成应用。 有关详细信息,请使用“版本”下拉列表选择本文的 5.0 或更高版本,并查看“JavaScript 模块中的 JavaScript 隔离”部分

<head> 标记中加载脚本

通常不建议使用本部分中的这个方法。

将 JavaScript (JS) 标记 (<script>...</script>) 置于<head>元素标记中:

<head>
    ...

    <script>
      window.jsMethod = (methodParameter) => {
        ...
      };
    </script>
</head>

<head> 加载 JS 并不是最好的方法,原因如下:

  • 如果脚本依赖于 Blazor,JS 互操作可能会失败。 建议使用一种其他方法加载脚本,而不是通过 <head> 标记加载脚本。
  • 由于分析脚本中的 JS 需要时间,页面的交互速度可能会变慢。

<body> 标记中加载脚本

将 JavaScript 标记 (<script>...</script>) 置于闭合</body>元素中 Blazor 脚本引用的后面:

<body>
    ...

    <script src="{BLAZOR SCRIPT}"></script>
    <script>
      window.jsMethod = (methodParameter) => {
        ...
      };
    </script>
</body>

在前面的示例中,{BLAZOR SCRIPT} 占位符是 Blazor 脚本路径和文件名。 有关脚本的位置,请参阅 ASP.NET Core Blazor 项目结构

从与组件并置的外部 JavaScript 文件 (.js) 加载脚本

为 Razor 组件并置 JavaScript (JS) 文件是整理应用中脚本的简便方法。

Blazor 应用的 Razor 组件使用 .razor.js 扩展名并置 JS 文件,并且可通过项目中文件的路径公开寻址:

{PATH}/{COMPONENT}.razor.js

  • 占位符 {PATH} 是指向组件的路径。
  • 占位符 {COMPONENT} 是组件。

发布应用后,框架会自动将脚本移动到 Web 根目录。 脚本将移动到 bin/Release/{TARGET FRAMEWORK MONIKER}/publish/wwwroot/{PATH}/{COMPONENT}.razor.js,其中占位符为:

无需更改脚本的相对 URL,因为 Blazor 会为你将文件 JS 放置在已发布的静态资产中。

本部分和以下示例主要侧重于说明 JS 文件并置。 第一个示例演示了具有普通 JS 函数的 JS 并置文件。 第二个示例演示了如何使用模块加载函数,这是建议用于大多数生产应用的方法。 ASP.NET Core 中从 .NET 调用 JavaScript 函数的方法Blazor中全面介绍了从 .NET 调用 JS,其中还详细介绍了 BlazorJS API 并提供了一些额外示例。 ASP.NET Core Razor 组件生命周期中介绍了第二个示例中出现的组件处置。

以下 JsCollocation1 组件通过 HeadContent 组件加载脚本,并使用 IJSRuntime.InvokeAsync 调用函数 JS。 占位符 {PATH} 是指向组件的路径。

重要

如果在测试应用中使用以下代码进行演示,请将 {PATH} 占位符更改为组件的路径(例如:在 .NET 8 或更高版本中为 Components/Pages,在 .NET 7 或更低版本中为 Pages)。 在 Blazor Web App(.NET 8 或更高版本)中,组件需要全局应用到应用程序或组件定义的交互式呈现模式。

在脚本 Blazor 后添加以下脚本(启动脚本 Blazor 的位置):

<script src="{PATH}/JsCollocation1.razor.js"></script>

JsCollocation1 组件 ({PATH}/JsCollocation1.razor):

@page "/js-collocation-1"
@inject IJSRuntime JS

<PageTitle>JS Collocation 1</PageTitle>

<h1>JS Collocation Example 1</h1>

<button @onclick="ShowPrompt">Call showPrompt1</button>

@if (!string.IsNullOrEmpty(result))
{
    <p>
        Hello @result!
    </p>
}

@code {
    private string? result;

    public async void ShowPrompt()
    {
        result = await JS.InvokeAsync<string>(
            "showPrompt1", "What's your name?");
        StateHasChanged();
    }
}

并置的 JS 文件放置在文件名为 JsCollocation1.razor.jsJsCollocation1 组件文件旁边。 在 JsCollocation1 组件中,在并置文件的路径中引用了该脚本。 在以下示例中,该 showPrompt1 函数接受来自 Window prompt() 的用户名称,并将其返回到组件 JsCollocation1 以进行显示。

{PATH}/JsCollocation1.razor.js:

function showPrompt1(message) {
  return prompt(message, 'Type your name here');
}

不建议在生产应用中普遍使用上述方法,因为它会污染具有全局功能的客户端。 生产应用适用的更好方法是使用 JS 模块。 相同的普遍原则适用于从并置的 JS 文件加载 JS 模块,如下一个示例所示。

以下 JsCollocation2 组件的 OnAfterRenderAsync 方法将 JS 模块加载到 module,这是组件类的一个 IJSObjectReferencemodule 用于调用 showPrompt2 函数。 占位符 {PATH} 是指向组件的路径。

重要

如果在测试应用中使用以下代码进行演示,请将占位符 {PATH} 更改为组件的路径。 在 Blazor Web App(.NET 8 或更高版本)中,组件需要全局应用到应用程序或组件定义的交互式呈现模式。

JsCollocation2 组件 ({PATH}/JsCollocation2.razor):

@page "/js-collocation-2"
@implements IAsyncDisposable
@inject IJSRuntime JS

<PageTitle>JS Collocation 2</PageTitle>

<h1>JS Collocation Example 2</h1>

<button @onclick="ShowPrompt">Call showPrompt2</button>

@if (!string.IsNullOrEmpty(result))
{
    <p>
        Hello @result!
    </p>
}

@code {
    private IJSObjectReference? module;
    private string? result;

    protected async override Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            /*
                Change the {PATH} placeholder in the next line to the path of
                the collocated JS file in the app. Examples:

                ./Components/Pages/JsCollocation2.razor.js (.NET 8 or later)
                ./Pages/JsCollocation2.razor.js (.NET 7 or earlier)
            */
            module = await JS.InvokeAsync<IJSObjectReference>("import",
                "./{PATH}/JsCollocation2.razor.js");
        }
    }

    public async void ShowPrompt()
    {
        if (module is not null)
        {
            result = await module.InvokeAsync<string>(
                "showPrompt2", "What's your name?");
            StateHasChanged();
        }
    }

    async ValueTask IAsyncDisposable.DisposeAsync()
    {
        if (module is not null)
        {
            try
            {
                await module.DisposeAsync();
            }
            catch (JSDisconnectedException)
            {
            }
        }
    }
}

在前面的示例中,JSDisconnectedException在模块处置过程中捕获,以防BlazorSignalR线路丢失。 如果在应用中使用了Blazor WebAssembly上述代码,则不会SignalR丢失任何连接,因此可以删除catchtry-该块并保留释放模块的行()。await module.DisposeAsync(); 有关详细信息,请参阅 ASP.NET Core BlazorJavaScript 互操作性(JS 互操作)

{PATH}/JsCollocation2.razor.js:

export function showPrompt2(message) {
  return prompt(message, 'Type your name here');
}

只有基于 IJSRuntime 接口的 Blazor 的 JS 互操作机制支持在 Razor 类库 (RCL) 中将脚本和模块用于并置的 JS。 如果要实现 JavaScript [JSImport]/[JSExport] 互操作,请参阅 JavaScript JSImport/JSExport 与 ASP.NET Core Blazor 互操作

对于由使用基于 IJSRuntime 的 JS 互操作的 Razor 类库 (RCL) 提供的脚本或模块,可使用以下路径:

./_content/{PACKAGE ID}/{PATH}/{COMPONENT}.{EXTENSION}.js

  • 若要创建 JS 文件的正确静态资产路径,需要当前目录 (./) 的路径段。
  • 占位符 {PACKAGE ID} 是 RCL 的包标识符(或由应用引用的类库的库名称)。
  • 占位符 {PATH} 是指向组件的路径。 如果 Razor 组件位于 RCL 的根目录下,则不包括路径段。
  • 占位符 {COMPONENT} 是组件名称。
  • 占位符 {EXTENSION} 与组件的扩展名匹配,为 razorcshtml

在以下 Blazor 应用示例中:

  • RCL 的包标识符是 AppJS
  • 将为 JsCollocation3 组件 (JsCollocation3.razor) 加载模块的脚本。
  • JsCollocation3 组件位于 RCL 的 Components/Pages 文件夹中。
module = await JS.InvokeAsync<IJSObjectReference>("import", 
    "./_content/AppJS/Components/Pages/JsCollocation3.razor.js");

有关 RCL 的详细信息,请参阅使用 Razor 类库 (RCL) 中的 ASP.NET Core Razor 组件

从外部 JavaScript 文件 (.js) 加载脚本

将带有脚本源 (src) 路径的 JavaScript (JS) 标记 (<script>...</script>) 置于闭合</body>元素中Blazor脚本引用的后面:

<body>
    ...

    <script src="{BLAZOR SCRIPT}"></script>
    <script src="{SCRIPT PATH AND FILE NAME (.js)}"></script>
</body>

前面示例中的占位符:

  • {BLAZOR SCRIPT} 占位符是 Blazor 脚本路径和文件名。 有关脚本的位置,请参阅 ASP.NET Core Blazor 项目结构
  • {SCRIPT PATH AND FILE NAME (.js)} 占位符是 wwwroot 下的路径和脚本文件名。

在以下前述 <script> 标记的示例中,scripts.js 文件位于应用的 wwwroot/js 文件夹中:

<script src="js/scripts.js"></script>

如果不希望将所有脚本都保留在wwwroot下的单独文件夹中,也可以直接从wwwroot文件夹中提供脚本:

<script src="scripts.js"></script>

当外部 JS 文件由 Razor 类库提供时,使用其稳定的静态 Web 资产路径 _content/{PACKAGE ID}/{SCRIPT PATH AND FILE NAME (.js)} 指定 JS 文件:

  • {PACKAGE ID} 占位符是库的包 ID。 如果项目文件中没有指定 <PackageId>,则包 ID 默认为项目的程序集名称。
  • {SCRIPT PATH AND FILE NAME (.js)} 占位符是 wwwroot 下的路径和文件名。
<body>
    ...

    <script src="{BLAZOR SCRIPT}"></script>
    <script src="_content/{PACKAGE ID}/{SCRIPT PATH AND FILE NAME (.js)}"></script>
</body>

在以下前述 <script> 标记的示例中:

  • Razor 类库的程序集名称为 ComponentLibrary,并且库的项目文件中没有指定 <PackageId>
  • scripts.js 文件位于类库的 wwwroot 文件夹中。
<script src="_content/ComponentLibrary/scripts.js"></script>

有关详细信息,请参阅使用 Razor 类库 (RCL) 中的 ASP.NET Core Razor 组件

在Blazor启动之前或之后注入脚本

要确保在Blazor启动之前或之后加载脚本,请使用 JavaScript 初始值设定项。 有关详细信息和示例,请参阅ASP.NET Core Blazor启动

在 Blazor 启动后注入脚本

要在Blazor启动后注入脚本,请链接到由手动启动Blazor产生的Promise。 有关详细信息和示例,请参阅ASP.NET Core Blazor启动

JavaScript 模块中的 JavaScript 隔离

Blazor 在标准JS模块中启用 JavaScript (JS) 隔离(ECMAScript 规范)。

JS 隔离具有以下优势:

  • 导入的 JS 不再污染全局命名空间。
  • 库和组件的使用者不需要导入相关的 JS。

在服务器端方案中,始终在线路丢失BlazorSignalR的情况下捕获JSDisconnectedException,防止JS互操作调用释放模块,从而导致未经处理的异常。 Blazor WebAssembly应用在互操作期间JS不使用SignalR连接,因此无需在Blazor WebAssembly应用中捕获JSDisconnectedException模块处置。

有关更多信息,请参阅以下资源: