前言
Blazor支持执行JavaScript脚本,通常是将脚本放在wwwroot/index.html
(Blazor WebAssembly)或Pages/_Host.cshtml
(Blazor Server)中。
但是,这种方式会将所有JS方法用全局函数加载,即使某些方法只需要在特定组件中使用。既影响加载性能,又会造成全局污染。
JavaScript隔离
1. 标准方式
从.NET 5.0开始,Blazor支持在标准JavaScript模块中启用 JavaScript隔离(https://docs.microsoft.com/zh-cn/aspnet/core/blazor/javascript-interoperability/call-javascript-from-dotnet?view=aspnetcore-5.0#javascript-isolation-in-javascript-modules)。
实现方式如下:
首先,在
wwwroot
目录下创建独立的js文件定义JS模块,比如scripts.js,
export function showPrompt(message) {return prompt(message, 'Type anything here');
}
在Blazor组件中,使用IJSRuntime将模块作为IJSObjectReference导入
然后,使用IJSObjectReference从模块调用导出的JS函数,代码如下:
@page "/fetchdata"
@inject IJSRuntime JS<p><button @onclick="TriggerPrompt">按需加载JavaScript脚本</button>
</p><p>@result
</p>@code {private IJSObjectReference module;private string result;protected override async Task OnAfterRenderAsync(bool firstRender){if (firstRender){module = await JS.InvokeAsync<IJSObjectReference>("import", "./scripts.js");}}private async Task TriggerPrompt(){result = await module.InvokeAsync<string>("showPrompt", "输入文字:");}
}
可以看到,JS文件在访问页面时才加载,并且运行正常:
2. libman方式
但是,这种方式有一个缺点,文件必须放在wwwroot
目录下。
我更希望,JS文件和对应的Blazor组件放在一起,类似这样:
右键单击项目,选择"管理客户端库",修改创建的libman.json文件内容如下:
{"version": "1.0","defaultProvider": "filesystem","libraries": [{"library": "Pages","files": ["FetchData.razor.js"],"destination": "wwwroot/"}]
}
右键单击libman.json,选择“生产时启用还原客户端库”。
修改FetchData.razor代码如下:
protected override async Task OnAfterRenderAsync(bool firstRender)
{if (firstRender){module = await JS.InvokeAsync<IJSObjectReference>("import","./FetchData.razor.js");}
}
再次运行, 工作正常。
3. 内嵌资源方式
上述方式虽然实现了效果,但是JS文件还是放在了wwwroot
目录下,只是工具帮你进行的复制操作。
如果你就是不希望wwwroot
下有多余文件,可以尝试下面的方式。
修改JS文件属性"生成操作",设置为"嵌入的资源"。
修改FetchData.razor代码如下, 将资源文件作为JS代码加载:
protected override async Task OnAfterRenderAsync(bool firstRender)
{if (firstRender){string scriptContent;using (var stream = this.GetType().Assembly.GetManifestResourceStream(this.GetType().Assembly.GetName().Name + ".Pages.FetchData.razor.js")){using (var sr = new System.IO.StreamReader(stream)){scriptContent = await sr.ReadToEndAsync();}}module = await JS.InvokeAsync<IJSObjectReference>("import", "data:text/javascript;base64," + Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(scriptContent)));}
}
再次运行, 同样工作正常,而且wwwroot
下没有增加文件。
结论
今天,我们介绍了按需加载JavaScript脚本的标准方式及2种变种,你可以按照你的喜好选择使用。
如果你觉得这篇文章对你有所启发,请关注我的个人公众号”My IO“,记住我!