Blazor Server:仅在特定页面上加载js脚本,而不是全部加载。

12

建议在 _Host.cshtml 中加载 JS 库,而不是在布局 (layout) 中(也不允许在组件 (component) 中加载)。

但这样做,如何为一个页面加载一些脚本文件,为另一个页面加载其它的脚本文件呢?例如,我只想在使用谷歌地图或数据表格的页面中加载它们,而不是在所有页面中都加载(浪费空间/内存/加载时间)。

谢谢!


3
Blazor是单页应用程序(SPA),因此只有一个页面,你需要将所有内容放在一个地方。_Host是正确的位置。 - H H
但也许你可以使用JS Interop进行一些动态加载,这不是我的领域。 - H H
Blazor是一个SPA:单页应用程序。那又怎样... Angular也是SPA。所以只有一个页面,你需要将所有内容都带上。你说呢? - enet
1
@SandroRiz,请耐心等待。它很快就会到来。我听说它将像Angular一样被实现。 - enet
3个回答

16

正如评论中所讨论的那样,Blazor是一个SPA,因此任何加载的脚本都可在Blazor页面上使用,因为它是同一页面。

然而,您不必在_Host.cshtml中列出所有脚本,实际上您可能只想在需要时加载特定脚本(例如,并非所有用户都使用需要该脚本的特定页面/组件)。

可以使用JS Interop动态加载脚本。我创建了以下的 scriptLoader.js 库并将其包含在_Host.cshtml中:

// loadScript: returns a promise that completes when the script loads
window.loadScript = function (scriptPath) {
    // check list - if already loaded we can ignore
    if (loaded[scriptPath]) {
        console.log(scriptPath + " already loaded");
        // return 'empty' promise
        return new this.Promise(function (resolve, reject) {
            resolve();
        });
    }

    return new Promise(function (resolve, reject) {
        // create JS library script element
        var script = document.createElement("script");
        script.src = scriptPath;
        script.type = "text/javascript";
        console.log(scriptPath + " created");

        // flag as loading/loaded
        loaded[scriptPath] = true;

        // if the script returns okay, return resolve
        script.onload = function () {
            console.log(scriptPath + " loaded ok");
            resolve(scriptPath);
        };

        // if it fails, return reject
        script.onerror = function () {
            console.log(scriptPath + " load failed");
            reject(scriptPath);
        }

        // scripts will load at end of body
        document["body"].appendChild(script);
    });
}
// store list of what scripts we've loaded
loaded = [];

这将创建一个script元素并将其附加到文档的body元素上。它返回一个Promise,因为脚本将异步加载,所以需要在C#代码中使用await

loaded数组存在是为了避免重新加载脚本。任何已加载的脚本都将保持加载状态,除非用户刷新页面。因此,加载只会发生一次。

在需要确保加载程序库的页面/组件上,我将需要注入IJSruntime...

@inject IJSRuntime jsRuntime

然后称之为..

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        // invoke script loader
        Console.WriteLine("Loading jQuery");
        await jsRuntime.InvokeVoidAsync("loadScript", "https://code.jquery.com/jquery-3.4.1.js");
        await jsRuntime.InvokeVoidAsync("loadScript", "myJQueryTest.js");

        Console.WriteLine("Invoking jQuery");

        await jsRuntime.InvokeVoidAsync("setH1", "Hello world!");

        Console.WriteLine("Invoked JQuery");

        await base.OnAfterRenderAsync(firstRender);
    }

myJQueryTest.js很简单:

window.setH1 = function (message) {
    $('h1').text(message);
}

演示存储库已创建:https://github.com/conficient/BlazorDynamicScriptLoad


2
另一个注意事项-我使用了一个复杂的库进行测试,它也加载了它自己的脚本,但这次测试失败了,因此它可能在某些情况下无法正常工作。对于简单的用例,它应该可以正常使用。 - Quango
你尝试过刷新页面吗?在你的动态脚本加载出现的页面上?我有一个非常类似的代码,只要我不刷新页面,它就能正常工作,但是当我刷新页面时,整个应用程序就不再正确响应了。我很期待使用你的代码,但目前还无法使其生效。 - Nicwin
我会检查日志/控制台以查找错误,您不应该刷新页面。 - Quango
优秀的解决方案!但是在使用同一组件的多个实例时会出现问题,请参见这里这里 - lonix
如果多次使用它,例如对于可以包含多次的组件,则会失败,因为存在竞争条件,其中组件不等待脚本加载。请参见此处以避免这种情况。 - lonix
显示剩余2条评论

4

还有一种使用JavaScript导出函数的方法来实现这一点。

wwwroot/js/script.js

export async function someFunction(parameter1, paramater2) {
        ...
  }

Pages/CallJavaScript.razor

@page "/call-js"
@inject IJSRuntime JS

@code{
  private IJSObjectReference myScript;

  protected override async Task OnAfterRenderAsync(bool firstRender)
  {
    if (firstRender)
    {
      myScript = await JS.InvokeAsync<IJSObjectReference>("import", "/js/script.js");
    }
  }

  private async Task CallJavaScriptMethod()
  {
    myScript.InvokeVoidAsync("someFunction", "param1", "param2");
  }

}

0

这个方法即使是通过 href 返回页面而不是 Navigationmanager 重定向(强制重新加载为 true)也可以工作。 如果您需要在页面加载时应用一些 js 到 dom 元素,则可以在 "firstrender" 部分中使用 CallJavascriptMethod。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接