mcpField
M
maui-hybridwebview

maui-hybridwebview

github.com/davidortinau开发17644 次安装16 周前更新
代码仓库

关于 maui-hybridwebview

面向 .NET MAUI 开发者的 HybridWebView 实用指南,介绍如何托管本地 Web 内容并连接 JavaScript 与 C#。重点涵盖互操作设置、序列化、异常处理和常见故障。

主要功能

  1. 01C# 与 JavaScript 类型化互操作
  2. 02原始消息交换
  3. 03源生成 JSON 序列化指南
  4. 04修剪与 NativeAOT 注意事项
  5. 05常见设置及运行时故障排查

适用场景

  1. 01排查 JavaScript 到 C# 调用失败
  2. 02选择类型化互操作或原始消息
  3. 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