关于 maui-hybridwebview
面向 .NET MAUI 开发者的 HybridWebView 实用指南,介绍如何托管本地 Web 内容并连接 JavaScript 与 C#。重点涵盖互操作设置、序列化、异常处理和常见故障。
主要功能
- 01C# 与 JavaScript 类型化互操作
- 02原始消息交换
- 03源生成 JSON 序列化指南
- 04修剪与 NativeAOT 注意事项
- 05常见设置及运行时故障排查
适用场景
- 01排查 JavaScript 到 C# 调用失败
- 02选择类型化互操作或原始消息
- 03为修剪配置互操作代码
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ davidortinau/ maui-skills --skill maui-hybridwebview安装后在 Claude Code 中输入/maui-hybridwebview确认已加载。
附带文件references/hybridwebview-api.md
详细介绍
.NET MAUI 中的 HybridWebView
HybridWebView 可在 .NET MAUI 应用中承载 HTML/JS/CSS 内容,并支持 C#↔JS 双向通信。它不是通用浏览器控件,而是专为随应用一起发布的本地 Web 内容设计的。
常见问题
| 问题 | 解决方法 |
|---|---|
| 屏幕一片空白 | Resources/Raw/wwwroot 中缺少 Web 资源,或未设置 DefaultFile |
| JS 互操作无声失败 | HTML 中缺少 <script src="_hwv/HybridWebView.js"></script> |
InvokeJavaScriptAsync 返回 null | 返回类型缺少 JsonSerializerContext 中的 [JsonSerializable] 特性 |
| JS → C# 调用无效 | JS 调用 C# 方法前未调用 SetInvokeJavaScriptTarget |
| 启用裁剪时发生序列化崩溃 | 未使用源生成的 JsonSerializerContext |
⚠️ 桥接脚本必不可少
HTML 页面必须在任何应用脚本之前包含桥接脚本:
<script src="_hwv/HybridWebView.js"></script>
<script src="scripts/app.js"></script>
<script src="scripts/app.js"></script>
<script src="_hwv/HybridWebView.js"></script>
JSON 序列化——每种类型都必须注册
InvokeJavaScriptAsync 中使用的每种参数类型和返回类型都必须有对应的 [JsonSerializable] 条目:
// ✅ Correct — all interop types registered
[JsonSerializable(typeof(int))]
[JsonSerializable(typeof(string))]
[JsonSerializable(typeof(Person))]
internal partial class MyJsonContext : JsonSerializerContext { }
// ❌ Wrong — adding a new type to interop without registering it
// This causes silent null returns or runtime exceptions
规则:向互操作接口添加新类型时,必须向上下文添加
[JsonSerializable(typeof(T))]特性。忘记这一步是导致互操作莫名失败的首要原因。
SetInvokeJavaScriptTarget——调用时机很重要
// ✅ Set target BEFORE the web page loads and JS calls C#
myHybridWebView.SetInvokeJavaScriptTarget(new MyJsBridge());
// ❌ Setting it after JS already tried to call — calls are lost
⚠️ 请在页面构造期间或 OnAppearing 中调用 SetInvokeJavaScriptTarget,不要等到需要时才调用。
异常处理(.NET 9+)
InvokeJavaScriptAsync 期间抛出的 JS 异常会转发到 .NET。互操作调用务必使用 try/catch 包装:
// ✅ Catches JS errors
try
{
var result = await myHybridWebView.InvokeJavaScriptAsync<string>(
"riskyFunction", MyJsonContext.Default.String);
}
catch (Exception ex)
{
Debug.WriteLine($"JS error: {ex.Message}");
}
// ❌ Unhandled JS exception crashes the interop pipeline
var result = await myHybridWebView.InvokeJavaScriptAsync<string>(
"riskyFunction", MyJsonContext.Default.String);
裁剪 / NativeAOT 注意事项
MAUI 项目默认禁用裁剪。如果启用裁剪:
- ⚠️ 必须使用源生成的
JsonSerializerContext(不能使用基于反射的序列化) - ⚠️ 将
JsonSerializerIsReflectionEnabledByDefault设为false - 无论裁剪设置如何,都建议使用上文所示的
JsonSerializerContext
<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
<JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>
决策框架——类型化互操作还是原始消息
| 需求 | 使用方式 |
|---|---|
| 需要类型安全的结构化数据交换 | 使用 JsonSerializerContext 的 InvokeJavaScriptAsync / InvokeDotNet |
| 简单字符串负载,即发即弃 | SendRawMessage / RawMessageReceived |
| 从 JS 调用 C# 并获取返回值 | InvokeDotNet(必须先设置目标) |
| 需要调用多个 JS 函数 | 类型化互操作——每个函数调用一次 InvokeJavaScriptAsync |
快速检查清单
- Web 内容位于
Resources/Raw/wwwroot下 -
index.html在应用脚本之前包含<script src="_hwv/HybridWebView.js"></script> - 已设置
DefaultFile(或使用默认值index.html) - 每种互操作类型都在某个
JsonSerializerContext中有对应的[JsonSerializable]条目 - JS 调用 C# 方法前已调用
SetInvokeJavaScriptTarget -
InvokeJavaScriptAsync调用已使用 try/catch 包装(.NET 9+) - 如果启用了裁剪:已配置源生成的 JSON 序列化
评论
支持 Markdown