Featured image of post 10版本ABP Bundle的兼容性问题

10版本ABP Bundle的兼容性问题

在10版本下,ABP Bundle的兼容性问题会导致Blazor WASM发布模式打不开。

Blazor WebAssembly + ABP 10.0.2 + .NET 10 预览版发布兼容性问题记录

结论:目前使用 .NET 10 + ABP 10.0.2 做 Blazor WASM 发布,会遇到脚本加载、Service Worker 缓存等框架级问题。短期更推荐使用 .NET 8 稳定版 + ABP 8,或在当前项目中采用少量手动脚本引用作为临时方案。

1. 现象:开发模式正常,发布模式前端白屏

  • 开发模式(dotnet run / IDE 启动):
    • Blazor WebAssembly 能正常加载。
    • 登录、OIDC、ABP 组件都工作正常。
  • 发布模式(dotnet publish 后从 bin/Release/net10.0/publish 启动):
    • 页面 HTML 能返回 200。
    • 静态资源(global.cssglobal.jsdotnet.js 等)大部分能返回 200。
    • 但最终前端不渲染内容,控制台出现异常。

典型错误日志:

  • 后端日志中多次出现 404:
    • _framework/WHS.Blazor.Client.jqrq8onpr6.wasm 404。
    • service-worker-assets.js 404。
  • 前端控制台错误:
    • Could not find 'AuthenticationService.init' ('AuthenticationService' was undefined).
    • 发生在 blazor.web.jscallEntryPoint 阶段。

2. Service Worker 导致的旧资源缓存问题

Blazor PWA 默认会:

  • 通过 service-worker.published.js 加载 service-worker-assets.js
  • 根据 assetsManifest 缓存 blazor.boot.json、wasm、dll、css、js 等资源。

在 .NET 10 + ABP 10.0.2 的组合下,遇到的问题是:

  • 首次发布后,Service Worker 会缓存一份旧的 blazor.boot.json 和 wasm 哈希。
  • 再次修改代码并发布时,即使服务器上已经是新 wasm 文件:
    • 浏览器仍优先从 SW 缓存中拿旧版本的 blazor.boot.json
    • 于是前端会请求不存在的 _framework/WHS.Blazor.Client.<old>.wasm,服务器返回 404。

临时解决办法:

  • App.razor 中对 localhost 特判,启动时注销所有已注册的 Service Worker:
1
2
3
4
5
6
7
8
9
const isLocalhost = location.hostname === 'localhost' || location.hostname === '127.0.0.1';
if ('serviceWorker' in navigator && isLocalhost) {
    window.addEventListener('load', async () => {
        const registrations = await navigator.serviceWorker.getRegistrations();
        for (const registration of registrations) {
            await registration.unregister();
        }
    });
}
  • 对于生产环境(非 localhost),仍按正常逻辑注册 service-worker.published.js,保留 PWA 能力。

3. ABP 核心 JS 未正确注入的问题

ABP Blazor 依赖一组核心 JS 文件,例如:

  • _content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js
  • _content/Volo.Abp.AspNetCore.Components.Web/libs/abp/js/abp.js
  • _content/Volo.Abp.AspNetCore.Components.Web/libs/abp/js/lang-utils.js
  • _content/Volo.Abp.AspNetCore.Components.Web/libs/abp/js/authentication-state-listener.js

按照 ABP 的设计,这些脚本通常通过:

  • AbpBundlingOptions + BlazorWebAssemblyStandardBundles.Scripts.Global
  • <!--ABP:Scripts--> + app.MapAbpStaticAssets()

在页面中自动注入。

在 .NET 10 + ABP 10.0.2 + Blazor 新的 “Razor Components + Interactive WebAssembly” 模式下,实际观测到:

  • 发布模式中,blazor.web.js 启动时,以上脚本可能尚未加载完成。
  • 结果:AuthenticationServiceabp 对象是 undefined,触发 AuthenticationService.init 找不到的错误。

临时解决办法:手动写死核心脚本引用,且放在 blazor.web.js 之前:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<body class="abp-application-layout">

    <!-- .NET 10 + ABP 10.0.2 兼容性修复:核心 JS 提前同步加载 -->
    <script src="_content/Microsoft.AspNetCore.Components.WebAssembly.Authentication/AuthenticationService.js"></script>
    <script src="_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/js/abp.js"></script>
    <script src="_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/js/lang-utils.js"></script>
    <script src="_content/Volo.Abp.AspNetCore.Components.Web/libs/abp/js/authentication-state-listener.js"></script>
    <script src="_framework/blazor.web.js"></script>

    <!-- 下面是应用容器和 ABP 自动注入的 global.js 等 -->
</body>

这样可以保证:

  • blazor.web.js 调用 .NET 入口点时,相关 JS API 已经在 window 中可用。

4. 为什么手动引用脚本是“临时方案”

优点:

  • 能迅速绕过当前版本的自动 Bundling/注入问题,让应用在发布模式下可用。
  • 更容易排查和控制脚本加载顺序。

缺点:

  • 失去 ABP Bundling 系统带来的自动合并、压缩、版本哈希等好处。
  • ABP 或 .NET 升级后,如果脚本路径或文件名变化,需要手动维护这些 <script>
  • 可能与 <!--ABP:Scripts--> 注入内容重复(需要注意不要加载两次相同脚本)。

因此,在代码中建议明确标注为“兼容性修复 / TODO”,后续框架修复后可以清理掉:

1
2
3
4
5
6
<!--
.NET 10 + ABP 10.0.2 发布模式兼容性修复:
手动写死核心 JS 文件, 确保在 blazor.web.js 启动前同步加载。
ABP 的自动脚本注入在发布模式下可能失效(时机 / 路径问题)。
TODO: 待 ABP 或 .NET 修复后,移除手动引用,恢复使用 ABP 自动注入。
-->

5. 小结

​ ABP 10.0.2 搭建 Blazor WebAssembly 在发布模式下确实存在一系列脚本和缓存的兼容性问题。在官方未完全打磨之前,“考虑回退到 .NET 8 稳定栈” 是工程上更务实的选择。

使用 Hugo 构建
主题 StackJimmy 设计