提問詞:【我想在一個dotnet core 8項目中使用 Blazor Hybric 技術,同時構建一個MAUI Project(A,打包到手機端),和一個Auto Render Model 的WebApp Project(W,發布到服務器),我想將兩者都用到的Razor文件抽出來作為一個CommonPage中間Project(C,被A和W引用),W中既需要有webAssembly的渲染方式也需要SSR的渲染方式,因為A項目中用到的razor頁面肯定是Client(WebAssembly)的渲染方式,我的問題是:C項目中每個Razor頁面的RenderModel是否還需要區分為多種方式,還是說只需要設置成webAssembly的方式就可以了,ssr所需的頁面組件單獨在W里設置?】

  deepseek思考過程:

  嗯,用戶的問題是關于在Blazor Hybrid項目中共享Razor組件,同時支持MAUI和WebApp項目,其中WebApp需要同時使用WebAssembly和SSR。他們想知道在公共項目C中,是否需要為每個Razor頁面區分不同的渲染模式,還是只需要用WebAssembly模式,然后在Web項目W中單獨處理SSR的部分。

  • 首先,我需要回憶Blazor的不同渲染模式。Blazor WebAssembly是客戶端渲染,SSR則是服務端渲染,而Blazor Hybrid在MAUI中可能使用WebView來呈現組件。公共項目C中的Razor組件需要被MAUI和Web項目共享,所以可能需要考慮兼容性。
  • 接下來,用戶提到Web項目W需要同時支持WebAssembly和SSR,這意味著某些頁面可能需要兩種渲染模式。但公共組件C被A和W引用,而A(MAUI)使用的是WebAssembly渲染模式。這時候可能需要確認在公共項目中設置渲染模式是否會影響不同項目的使用。
  • 根據Blazor的文檔,從.NET 8開始,組件可以定義渲染模式,比如使用@rendermode指令。如果公共組件C中的頁面設置為WebAssembly,那么在SSR環境下可能無法直接使用,除非進行適配。或者,可能需要在W項目中覆蓋或調整這些渲染模式。
  • 可能的解決方案是,公共項目C中的組件不指定渲染模式,或者設置為自動(Auto),這樣在Web項目W中可以根據需要設置為WebAssembly或SSR。而MAUI項目A可能默認使用WebAssembly模式,因此不需要特別設置。
  • 同時,需要考慮組件是否依賴于特定渲染模式的功能。例如,如果組件使用了只能在WebAssembly中運行的JS互操作,那么在SSR中可能會有問題。因此,公共組件需要保持中立,避免依賴特定模式的功能,或者在W項目中通過條件編譯或其他方式處理差異。
  • 另外,Web項目W可能需要將部分組件單獨設置為SSR,這時候可以在W項目中創建新的組件,繼承或引用公共組件,并添加相應的渲染模式。這樣公共組件保持通用,而特定渲染模式的調整在各自項目中處理。
  • 總結來說,公共項目C中的組件應盡量保持渲染模式中立,或者默認設置為適用于所有目標環境的模式(如WebAssembly),然后在需要不同渲染模式的項目中,通過覆蓋或指定模式來調整。這樣既保持組件的可重用性,又允許各項目根據需求進行定制。

  最終它給了我七個策略設計方案,驚艷到我,故在此記錄下來:


1. 公共組件庫(Project C)的設計原則

 

  • 不硬編碼渲染模式:在共享的 Razor 組件中不要使用 @rendermode 指令或硬編碼 RenderMode,保持渲染模式的中立性

 

  • 依賴注入兼容:避免在組件中直接依賴 HttpContext 等僅 SSR 可用的服務
  • 條件編譯處理平臺差異:用 #if MAUI 和 #if WEB 隔離平臺特定邏輯
@* SharedComponent.razor *@
@{
    var isMobile = 
  #if MAUI
        true;
  #else
        false;
  #endif
}
@if (isMobile)
{
    <MobileOnlyFeature />
}

2. MAUI 項目(Project A)配置

  • 強制指定渲染模式:在 _Imports.razor 中添加全局模式聲明

@rendermode RenderMode.InteractiveWebAssembly

3. Web 項目(Project W)的渲染模式策略

  • 創建渲染模式包裝組件:在 Web 項目中創建適配層

// Web/Components/SSRWrapper.razor
@inherits SharedComponent
@rendermode RenderMode.InteractiveServer

// Web/Components/WASMWrapper.razor 
@inherits SharedComponent
@rendermode RenderMode.InteractiveWebAssembly
  • 動態模式選擇:在路由配置中根據條件切換

@page "/dynamic-page"
@if (isSSR)
{
    <SSRWrapper />
}
else
{
    <WASMWrapper />
}

@code {
    private bool isSSR = /* 根據請求判斷是否需要 SSR */;
}

4. 構建配置優化

在公共組件庫的 .csproj 中配置多目標框架:

5. 服務注冊策略

在共享庫中定義接口,各平臺實現:

// Shared
public interface IPlatformService
{
    Task<string> GetPlatformInfo();
}

// MAUI
public class MauiService : IPlatformService { ... }

// Web
public class WebService : IPlatformService { ... }

6. 性能優化手段

  • Tree Shaking:在公共庫中使用 [DynamicDependency] 特性

[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(MobileOnlyComponent))]
public class SharedComponent : ComponentBase

7. 調試方案

在 .vscode/launch.json 中配置組合調試:

{
    "configurations": [
        {
            "name": "Debug MAUI + Web",
            "type": "blazorwasm",
            "request": "launch",
            "browser": "edge",
            "hotReload": true,
            "projects": [
                "ProjectA.csproj",
                "ProjectW.csproj"
            ]
        }
    ]
}

這種架構的關鍵點在于:

    1. 公共組件保持渲染模式不可知

    2. 各消費項目通過包裝組件和構建配置控制最終渲染方式

    3. 通過條件編譯和接口隔離平臺特定邏輯

    4. 利用現代 .NET 的多目標框架特性實現代碼最大復用

對于需要 SSR 和 WASM 同時存在的頁面,建議在 Web 項目中創建路由代理組件:

@page "/hybrid-page"
@rendermode RenderMode.InteractiveAuto

<SharedComponent @rendermode="currentMode" />

@code {
    private IComponentRenderMode currentMode => 
        ShouldUseSSR() ? RenderMode.InteractiveServer : RenderMode.InteractiveWebAssembly;
}

這種方案比簡單分離渲染模式更靈活,可以:

  • 根據設備類型自動切換
  • 實現漸進式增強(先 SSR 再水合)
  • 支持 A/B 測試不同渲染模式

最后建議在解決方案中添加架構測試:

[Test]
public void SharedComponents_ShouldNotReferenceRenderingSpecificServices()
{
    var sharedAssembly = typeof(SharedComponent).Assembly;
    
    Assert.That(sharedAssembly.GetTypes()
        .SelectMany(t => t.GetMethods())
        .Where(m => m.GetParameters()
            .Any(p => p.ParameterType == typeof(HttpContext))),
        Is.Empty);
}