看過 Tauri 後,繼續探索將網站轉成桌面小工具的主題。想利用前端技術寫桌面程式,但 Electron 包了 Chromium 太肥,借用內建瀏覽器輕量化的想法大家都有,這篇來看微軟的解決方案 - MAUI HybridWebView。

HybridWebView 是 .NET MAUI 技術框架提供的控制項,可在原生 Web 檢視中載入 HTML、JavaScript 與 CSS 內容,並讓 JavaScript 與 C# 雙向通訊。例如,既有的 Vue/React/jQuery 應用程式可直接嵌入跨平台 MAUI 應用程式,後端則以 C# 與 .NET 實作 [^1]。

Web 內容預設放在 Resources/Raw/wwwroot,並以 index.html 作為預設檔案。整個應用程式連同 Web 內容會封裝在裝置本機執行,不一定需要連線外部網路。C# 端透過 InvokeJavaScriptAsync 呼叫 JavaScript 方法,JavaScript 端則以 window.HybridWebView.InvokeDotNet 呼叫 C# 方法。這個做法,也呼應了上回 Tauri 的 Invoke() 方法,意味著若要同一套前端介面同時用於網站及桌面程式,需要適度改寫將前後端溝通部分抽取出來,寫成兩套模組,再視情境抽換。

同樣做法,在 Github Copilot 要求 Opus 5.5 參考我的 ASP.NET Core 轉桌面小工具文章跟範例專案,改寫成 MAUI HybridWebView 版進行對照,有助於更快理解其架構。

  • AesEncryptorMaui/
    • AesEncryptorMaui.csproj:MAUI 專案設定,只鎖定 Windows / Mac Catalyst 桌面平台
    • MauiProgram.cs:App 進入點與 DI 設定,Debug 時開啟 WebView DevTools
    • App.xaml / App.xaml.cs:建立唯一的主視窗,設定標題與尺寸
    • MainPage.xaml / MainPage.xaml.cs:放置 HybridWebView,並指定 JS 可呼叫的 .NET 物件
    • AesBridge.cs:提供給 JS 呼叫的方法,對應原本的 MapPost("/aes")
      public sealed class AesBridge
      {
        public AesResult Aes(string mode, string key, string data)
        {
        	var encMode = mode != "decrypt";
        	var result = new AesResult();
        	try
        	{
        		if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(data))
        			throw new ArgumentException("parameter missing");
        		result.Result = encMode ? CodecNetFx.AesEncrypt(key, data) : CodecNetFx.AesDecrypt(key, data);
        	}
        	catch (Exception ex) { result.Message = ex.Message; }
        	return result;
         }
      }
      
      public sealed class AesResult
      {
        public string Result { get; set; } = string.Empty;
        public string Message { get; set; } = string.Empty;
      }
      
    • CodecNetFx.cs:AES 加解密,沿用原範例
    • Resources/Raw/wwwroot/:前端網頁資源
    • index.html:前端介面,使用 Vue 3
      async Run(mode, data, resProp) {
          try {
              // 取代原本 <form action="/aes" target="iframe"> 的 POST
              const res = await window.HybridWebView.InvokeDotNet('Aes', [mode, this.EncKey, data]);
              this[resProp] = res.Result;
              this.Message = res.Message;
              this.Highlight(resProp);
          }
          catch (e) {
              this.Message = e.message || String(e);
          }
      },
      
      • vue.global.prod.min.js:Vue 程式庫壓縮檔
    • Platforms/Windows/App.xaml.cs:設定 WebView2 使用者資料夾
      public App()
      {
        // 未封裝 (unpackaged) 的 exe 若被安裝到 Program Files,WebView2 無法在 exe 目錄寫入快取,
        // 故在 WebView 建立前將使用者資料夾指到可寫入的 AppData
        var userDataFolder = Path.Combine(FileSystem.AppDataDirectory, "WebView2");
        Environment.SetEnvironmentVariable("WEBVIEW2_USER_DATA_FOLDER", userDataFolder);
      
        this.InitializeComponent();
      }
      

成功執行!

將網頁裝進 .exe 執行沒問題,但直接說結論:MAUI HybridWebView 較適合一般應用程式,不適合用來寫桌面小工具。

原因是體積。雖然使用作業系統內建的 WebView (Windows:WebView2 (Chromium Edge)、Mac Catalyst / iOS:WKWebView (Safari WebKit) 、Android:android.webkit.WebView (Chromium)),但開發時需要 dotnet workload install maui 安裝 MAUI Workload,發佈時若連同 .NET 10 + Windows App Runtime 1.8 打包,體積會高達 157MB,若不含 Windows App SDK(WindowsAppSDKSelfContained=false)約 70MB。參考 在很多企業環境,客戶端若已統一部署 .NET Runtime,WinForm / WPF 有機會選擇 Runtime Dependency 只部署程式本體,將體積縮小到數 MB,卻不太能期望客戶端已裝好 Windows App Runtime (winget install Microsoft.WindowsAppRuntime.1.8),會變成需要額外部署及確認,或改用 MSIX、傳統安裝程式 (WiX、Inno Setup),無法實現單一檔案,隨 Copy 隨用。想打包桌面小工具,宜另謀他法。

MAUI HybridWebView lets you embed web frontends in a cross-platform .NET desktop app with two-way JS and C# communication. It works well, but packaged sizes reach 70 to 157MB, making it unsuitable for lightweight desktop tools.


Comments

Be the first to post a comment

Post a comment