ASP.NET Core Blazor 漸進式 Web 應用程式 (PWA)
注意
這不是這篇文章的最新版本。 如需目前版本,請參閱本文的 .NET 8 版本。
警告
不再支援此版本的 ASP.NET Core。 如需詳細資訊,請參閱 .NET 和 .NET Core 支援原則。 如需目前版本,請參閱本文的 .NET 8 版本。
Blazor 漸進式 Web 應用程式 (PWA) 是單頁應用程式 (SPA),其使用新式瀏覽器 API 和功能,以如同傳統型應用程式一樣運作。
Blazor WebAssembly 是標準型用戶端 Web 應用程式平台,因此可以使用任何瀏覽器 API,包括下列功能所需的 PWA API:
- 離線工作並立即載入,與網路速度無關。
- 在自己的應用程式視窗中執行,而不只是瀏覽器視窗。
- 從主機的作業系統開始功能表、dock 列或 home 畫面啟動。
- 即使使用者未使用應用程式,仍從後端伺服器接收推播通知。
- 在背景中自動更新。
漸進一字是用來描述這些應用程式,因為:
- 使用者可能會先探索並使用其網頁瀏覽器中的應用程式,就像任何其他 SPA 一樣。
- 稍後,使用者會繼續在其 OS 中加以安裝,並啟用推播通知。
從 PWA 範本建立專案
建立新的 Blazor WebAssembly 應用程式時,請選取 [漸進式 Web 應用程式] 核取方塊。
您可以選擇性地為從 ASP.NET Core 託管Blazor WebAssembly專案範本建立的應用程式設定 PWA。 PWA 案例與裝載模型無關。
將現有的 Blazor WebAssembly 應用程式轉換成 PWA
遵循本節中的指引,將現有的 Blazor WebAssembly 應用程式轉換成 PWA。
在應用程式的專案檔中:
將下列
ServiceWorkerAssetsManifest
屬性新增至PropertyGroup
:... <ServiceWorkerAssetsManifest>service-worker-assets.js</ServiceWorkerAssetsManifest> </PropertyGroup>
將下列
ServiceWorker
項目新增至ItemGroup
:<ItemGroup> <ServiceWorker Include="wwwroot\service-worker.js" PublishedContent="wwwroot\service-worker.published.js" /> </ItemGroup>
若要取得靜態資產,請使用下列其中一種方法:
在命令殼層中使用
dotnet new
命令來建立個別的新 PWA 專案:dotnet new blazorwasm -o MyBlazorPwa --pwa
在上述命令中,
-o|--output
選項會為名為MyBlazorPwa
的應用程式建立新資料夾。如果您未轉換最新版本的應用程式,請傳遞
-f|--framework
選項。 下列範例會建立 ASP.NET Core 5.0 版的應用程式:dotnet new blazorwasm -o MyBlazorPwa --pwa -f net5.0
導覽至位於下列 URL 的 ASP.NET Core GitHub 存放庫,其連結至
main
分支參考來源和資產。 從套用至應用程式的 [切換分支] 或 [標籤] 下拉式清單中,選取您正在使用的版本。Blazor WebAssembly 專案範本
wwwroot
資料夾 (dotnet/aspnetcore
GitHub 存放庫main
分支)注意
.NET 參考來源的文件連結通常會載入存放庫的預設分支,這表示下一版 .NET 的目前開發。 若要選取特定版本的標籤,請使用 [切換分支或標籤] 下拉式清單。 如需詳細資訊,請參閱如何選取 ASP.NET Core 原始程式碼 (dotnet/AspNetCore.Docs #26205) 的版本標籤。
從您所建立的應用程式中,或從
dotnet/aspnetcore
GitHub 存放庫中參考資產的來源wwwroot
資料夾,將下列檔案複製到應用程式的wwwroot
資料夾中:icon-192.png
icon-512.png
manifest.webmanifest
service-worker.js
service-worker.published.js
在應用程式的 wwwroot/index.html
檔案中:
新增資訊清單和應用程式圖示的
<link>
元素:<link href="manifest.webmanifest" rel="manifest" /> <link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" /> <link rel="apple-touch-icon" sizes="192x192" href="icon-192.png" />
導覽至位於下列 URL 的 ASP.NET Core GitHub 存放庫,其連結至
release/7.0
分支參考來源和資產。 如果您使用 7.0 之後的 ASP.NET Core 版本,請變更文件版本選取器,以查看經過更新的本節指導。 從套用至應用程式的 [切換分支] 或 [標籤] 下拉式清單中,選取您正在使用的版本。Blazor WebAssembly 專案範本
wwwroot
資料夾 (dotnet/aspnetcore
GitHub 存放庫release/7.0
分支)注意
.NET 參考來源的文件連結通常會載入存放庫的預設分支,這表示下一版 .NET 的目前開發。 若要選取特定版本的標籤,請使用 [切換分支或標籤] 下拉式清單。 如需詳細資訊,請參閱如何選取 ASP.NET Core 原始程式碼 (dotnet/AspNetCore.Docs #26205) 的版本標籤。
從您所建立的應用程式中,或從
dotnet/aspnetcore
GitHub 存放庫中參考資產的來源wwwroot
資料夾,將下列檔案複製到應用程式的wwwroot
資料夾中:favicon.png
icon-512.png
manifest.json
service-worker.js
service-worker.published.js
在應用程式的 wwwroot/index.html
檔案中:
新增資訊清單和應用程式圖示的
<link>
元素:<link href="manifest.json" rel="manifest" /> <link rel="apple-touch-icon" sizes="512x512" href="icon-512.png" />
緊接在
blazor.webassembly.js
指令碼標籤後面的結尾</body>
標籤內新增下列<script>
標籤:... <script>navigator.serviceWorker.register('service-worker.js');</script> </body>
安裝和應用程式資訊清單
造訪使用 PWA 範本建立的應用程式時,使用者可以選擇將應用程式安裝到作業系統的 [開始] 功能表、dock 列或 home 畫面。 這個選項呈現的方式取決於使用者的瀏覽器。 使用桌面 Chromium 型瀏覽器 (例如 Edge 或 Chrome) 時,URL 列中會出現 [新增] 按鈕。 使用者選取 [新增] 按鈕之後,他們會收到確認對話方塊:
在 iOS 上,訪客可以使用 Safari 的 共用 按鈕及其 新增至主螢幕 選項來安裝 PWA。 在 Android 版的 Chrome 上,使用者應該選取右上角的 [功能表] 按鈕,後面接著 [新增至 Home畫面]。
安裝之後,應用程式會出現在自己的視窗中,但沒有網址列:
若要自訂視窗的標題、色彩配置、圖示或其他詳細資料,請參閱專案 wwwroot
目錄中的 manifest.json
檔案。 此檔案的結構描述是由 Web 標準所定義。 如需詳細資訊,請參閱 MDN Web 文件:Web 應用程式資訊清單。
離線支援
使用 PWA 範本選項建立的應用程式支援離線執行。 使用者在線上時必須先瀏覽應用程式。 瀏覽器會自動下載並快取離線操作所需的所有資源。
重要
開發支援會干擾進行變更及測試變更的一般開發週期。 因此,僅針對已發佈的應用程式啟用離線支援。
警告
如果您想要散發已啟用離線的 PWA,有數個重要的警告和注意事項。 這些是離線 PWA 的固有案例,而不是專屬於 Blazor。 請務必先閱讀並了解這些注意事項後,再假設您啟用離線應用程式的運作方式。
若要查看離線支援的運作方式:
發行應用程式。 如需詳細資訊,請參閱裝載和部署 ASP.NET Core Blazor。
將應用程式部署至支援 HTTPS 的伺服器,並在瀏覽器中存取其安全 HTTPS 位址的應用程式。
開啟瀏覽器的開發工具,並確認已在 [應用程式] 索引標籤上註冊主機的 [服務背景工作角色]:
重新載入頁面並檢查 [網路] 索引標籤。[服務背景工作角色] 或 [記憶體快取] 會列為所有頁面資產的來源:
若要確認瀏覽器與載入應用程式的網路存取無關,請執行下列其中一項動作:
- 關閉網頁伺服器,並查看應用程式如何持續正常運作,其中包括重載頁面。 同樣地,當網路連線緩慢時,應用程式會繼續正常運作。
- 指示瀏覽器在 [網路] 索引標籤中模擬離線模式:
使用服務背景工作角色的離線支援是 Web 標準,而不是專屬於 Blazor。 如需服務背景工作角色的詳細資訊,請參閱 MDN Web 文件:服務背景工作角色 API。 若要深入了解服務背景工作角色的常見使用模式,請參閱 Google Web:服務背景工作角色生命週期。
Blazor 的 PWA 範本會產生兩個服務背景工作角色檔案:
wwwroot/service-worker.js
,會在開發期間使用。wwwroot/service-worker.published.js
,會在發佈應用程式之後使用。
若要在兩個服務背景工作角色檔案之間共用邏輯,請考慮下列方法:
- 新增第三個 JavaScript 檔案來保存一般邏輯。
- 使用
self.importScripts
將一般邏輯載入這兩個服務背景工作角色檔案中。
快取優先擷取策略
內建 service-worker.published.js
服務背景工作角色會使用快取優先策略來解析要求。 這表示服務背景工作角色偏好傳回快取的內容,無論使用者是否具有網路存取權或伺服器上是否有較新的內容。
快取優先策略很有價值,因為:
其可確保可靠性。 網路存取不是布林狀態。 使用者不只是線上或離線:
- 使用者的裝置可能會假設其已上線,但網路可能太慢,等待下去行不通。
- 網路可能會傳回特定 URL 的無效結果,例如當有目前封鎖或重新導向特定要求的 CAPTIVE WIFI 入口網站時。
這就是瀏覽器的
navigator.onLine
API 不可靠且不應該相依的原因。其可確保正確性。 建置離線資源的快取時,服務背景工作角色會使用內容雜湊來確保其在單一時間擷取完整且自我一致的資源快照集。 接著,此快取會當做不可部分完成的單位來使用。 要求網路取得較新的資源並沒有意義,因為唯一需要的版本是已快取的版本。 任何其他項目都可能造成不一致和不相容的風險 (例如,嘗試使用未一起編譯的 .NET 組件版本)。
如果您必須防止瀏覽器從其 HTTP 快取中擷取 service-worker-assets.js
,例如,若要在部署新版本的服務背景工作角色時解決暫時完整性檢查失敗,請更新 wwwroot/index.html
中的服務背景工作角色註冊,並將 updateViaCache
設定為 'none':
<script>
navigator.serviceWorker.register('/service-worker.js', {updateViaCache: 'none'});
</script>
背景更新
身為心理模型,您可以將離線優先 PWA 視為可安裝的行動應用程式。 不論網路連線能力為何,應用程式都會立即啟動,但已安裝的應用程式邏輯會來自可能不是最新版本的時間點快照集。
Blazor PWA 範本會產生應用程式,每當使用者瀏覽且具有工作網路連線時,就會自動嘗試在背景自我更新。 其運作方式如下:
- 在編譯期間,專案會產生名為
service-worker-assets.js
的服務背景工作角色資產資訊清單。 資訊清單會列出應用程式離線運作所需的所有靜態資源,例如 .NET 組件、JavaScript 檔案和 CSS,包括其內容雜湊。 資源清單會由服務背景工作角色載入,以便得知要快取的資源。 - 每次使用者瀏覽應用程式時,瀏覽器會在背景中重新要求
service-worker.js
和service-worker-assets.js
。 檔案會與現有的已安裝服務背景工作角色逐一比較位元組。 如果伺服器傳回這些檔案其中一個的變更內容,服務背景工作角色會嘗試安裝本身的新版本。 - 安裝本身的新版本時,服務背景工作角色會為離線資源建立新的個別快取,並開始以
service-worker-assets.js
中所列的資源填入快取。 此邏輯會在service-worker.published.js
內的onInstall
函式中進行實作。 - 當載入所有資源而未發生任何錯誤,且所有內容雜湊相符時,處理序就會順利完成。 如果成功,新的服務背景工作角色就會進入等候啟用狀態。 一旦使用者關閉應用程式 (沒有剩餘的應用程式索引標籤或視窗),新的服務背景工作角色就會變成作用中,並用於後續的應用程式瀏覽。 刪除舊的服務背景工作角色及其快取。
- 如果處理序未順利完成,則會捨棄新的服務背景工作角色執行個體。 當用戶端有更好的網路連線可以完成要求時,會在使用者下一次造訪時,再次嘗試更新處理序。
編輯服務背景工作角色邏輯來自訂此處理序。 上述行為都不是 Blazor 特定的,而只是 PWA 範本選項所提供的預設體驗。 如需詳細資訊,請參閱 MDN Web 文件:服務背景工作角色 API。
如何解析要求
如快取優先擷取策略一節所述,預設服務背景工作角色會使用快取優先策略,這表示其會在可用時嘗試提供快取的內容。 如果沒有針對特定 URL 快取的內容 (例如,從後端 API 要求資料時),服務背景工作角色就會回復為一般網路要求。 如果伺服器可連線,網路要求就會成功。 此邏輯會在 service-worker.published.js
內的 onFetch
函式內實作。
如果應用程式的 Razor 元件仰賴從後端 API 要求資料,且您想要為因網路無法使用而失敗要求提供易記使用者體驗,請在應用程式的元件內實作邏輯。 例如,在 HttpClient 要求周圍使用 try/catch
。
支援伺服器轉譯的頁面
請考慮當使用者第一次瀏覽至 URL 時會發生什麼事,例如 /counter
,或應用程式中的任何其他深層連結。 在這些情況下,您不需要傳回快取為 /counter
的內容,而是需要瀏覽器載入快取為 /index.html
的內容,以啟動您的 Blazor WebAssembly 應用程式。 這些初始要求稱為瀏覽要求,而不是:
- 影像、樣式表單或其他檔案的
subresource
要求。 - API 資料的
fetch/XHR
要求。
預設服務背景工作角色包含瀏覽要求的特殊案例邏輯。 不論要求的 URL 為何,服務背景工作角色都會傳回 /index.html
的快取內容來解析要求。 此邏輯會在 service-worker.published.js
內的 onFetch
函式中進行實作。
如果您的應用程式有某些 URL 必須傳回伺服器轉譯的 HTML,而不是從快取提供 /index.html
,則您必須編輯服務背景工作角色中的邏輯。 如果包含 /Identity/
的所有 URL 都必須當作對伺服器的一般線上唯一要求來處理,請修改 service-worker.published.js
onFetch
邏輯。 尋找下列程式碼:
const shouldServeIndexHtml = event.request.mode === 'navigate';
將程式碼變更為下列項目:
const shouldServeIndexHtml = event.request.mode === 'navigate'
&& !event.request.url.includes('/Identity/');
如果您未這麼做,則不論網路連線能力為何,服務背景工作角色都會攔截這類 URL 的要求,並使用 /index.html
加以解析。
將外部驗證提供者的其他端點新增至檢查。 在下列範例中,會將 Google 驗證的 /signin-google
新增至檢查:
const shouldServeIndexHtml = event.request.mode === 'navigate'
&& !event.request.url.includes('/Identity/')
&& !event.request.url.includes('/signin-google');
Development
環境不需要採取任何動作,其一律會從網路擷取內容。
控制資產快取
如果您的專案定義 ServiceWorkerAssetsManifest
MSBuild 屬性,Blazor 的建置工具就會產生具有指定名稱的服務背景工作角色資產資訊清單。 預設 PWA 範本會產生包含下列屬性的專案檔:
<ServiceWorkerAssetsManifest>service-worker-assets.js</ServiceWorkerAssetsManifest>
檔案會放在 wwwroot
輸出目錄中,因此瀏覽器可要求 /service-worker-assets.js
以擷取此檔案。 若要查看此檔案的內容,請在文字編輯器中開啟 /bin/Debug/{TARGET FRAMEWORK}/wwwroot/service-worker-assets.js
。 不過,請勿編輯檔案,因為檔案會在每個組建上重新產生。
資訊清單會列出:
- 任何 Blazor 受控資源,例如 .NET 組件和離線運作所需的 .NET WebAssembly 執行階段檔案。
- 發佈至應用程式
wwwroot
目錄的所有資源,例如影像、樣式表單和 JavaScript 檔案,包括外部專案和 NuGet 套件所提供的靜態 Web 資產。
您可以編輯 service-worker.published.js
中 onInstall
的邏輯,以控制服務背景工作角色擷取及快取這些資源中的哪一個資源。 服務背景工作角色會擷取並快取符合一般 Web 副檔名的檔案,例如 .html
、.css
、.js
和 .wasm
,以及專屬於 Blazor WebAssembly 的檔案類型,例如 .pdb
檔案 (所有版本) 和 .dll
檔案 (.NET 7 或更早版本中的 ASP.NET Core)。
若要包含應用程式 wwwroot
目錄中所沒有的其他資源,請定義額外的 MSBuild ItemGroup
項目,如下列範例所示:
<ItemGroup>
<ServiceWorkerAssetsManifestItem Include="MyDirectory\AnotherFile.json"
RelativePath="MyDirectory\AnotherFile.json" AssetUrl="files/AnotherFile.json" />
</ItemGroup>
AssetUrl
中繼資料會指定要擷取資源以進行快取時,瀏覽器應該使用的基底相對 URL。 這可能與磁碟上的原始來原始檔案名稱無關。
重要
新增 ServiceWorkerAssetsManifestItem
並不會導致檔案在應用程式的 wwwroot
目錄中發佈。 發行輸出必須分開控制。 ServiceWorkerAssetsManifestItem
只會使其他項目出現在服務背景工作角色資產資訊清單中。
推播通知
如同任何其他 PWA,Blazor WebAssembly PWA 可從後端伺服器接收推播通知。 伺服器可以隨時傳送推播通知,即使在使用者未主動使用應用程式時也一樣。 例如,當不同的使用者執行相關動作時,可以傳送推播通知。
傳送推播通知的機制完全與 Blazor WebAssembly 無關,因為其為由後端伺服器實作,可使用任何技術。 如果您想要從 ASP.NET Core 伺服器傳送推播通知,請考慮使用與 Blazing Pizza 研討會中所採用方法類似的技術。
在用戶端上接收及顯示推播通知的機制也與 Blazor WebAssembly 無關,因為其已在服務背景工作角色 JavaScript 檔案中實作。 如需範例,請參閱 Blazing Pizza 研討會中使用的方法。
離線 PWA 的注意事項
並非所有應用程式都應嘗試支援離線使用。 離線支援會大幅增加複雜度,同時不一定與所需的使用案例相關。
離線支援通常只關於:
- 如果主要資料存放區是瀏覽器的本機位置。 例如,此方法在應用程式中與 IoT 裝置的 UI 相關,該裝置會將資料儲存在
localStorage
或 IndexedDB 中。 - 如果應用程式執行大量工作來擷取及快取與每個使用者相關的後端 API 資料,讓他們可以離線瀏覽資料。 如果應用程式必須支援編輯,則必須建置追蹤變更和同步處理資料與後端的系統。
- 如果目標是保證應用程式立即載入,而不論網路狀況為何。 在後端 API 要求周圍實作適當的使用者體驗,以顯示要求進度,並在要求因網路無法使用而失敗時正常運作。
此外,具備離線功能的 PWA 必須處理一系列額外的複雜功能。 開發人員應仔細熟悉下列各節中的注意事項。
只有在發佈時才離線支援
在開發期間,您通常需要查看瀏覽器中立即反映的每個變更,而不需經過背景更新處理序。 因此,Blazor 的 PWA 範本僅在發佈時才會啟用離線支援。
建置具備離線功能的應用程式時,在 Development
環境中測試應用程式並不夠。 您必須以應用程式的已發佈狀態來測試應用程式,以了解應用程式如何回應不同的網路狀況。
在使用者瀏覽離開應用程式之後更新完成
更新不會完成,直到使用者從所有索引標籤中的應用程式瀏覽離開為止。 如背景更新一節所述,將更新部署至應用程式之後,瀏覽器會擷取更新的服務背景工作角色檔案,以開始更新處理序。
令許多開發人員感到意外的是,即使此更新完成時,要在使用者已瀏覽離開所有索引標籤之後,更新才會生效。 即使其是唯一顯示應用程式的索引標籤,也不足以重新整理顯示應用程式的索引標籤。 在您的應用程式完全關閉之前,新的服務背景工作角色會維持在 [等候啟動] 狀態中。 這並非專屬於 Blazor,而是標準的 Web 平台行為。
這通常會使嘗試測試其服務背景工作角色或離線快取資源更新的開發人員感到困擾。 如果您簽入瀏覽器的開發人員工具,可能會看到類似下列內容:
只要顯示應用程式的索引標籤或視窗的「用戶端」清單不是空的,背景工作角色就會繼續等候。 服務背景工作角色這麼做的原因是保證一致性。 一致性表示會從相同的不可部分完成快取擷取所有資源。
測試變更時,您可能會發現選取如上一個螢幕擷取畫面所示的 [skipWaiting] 連結,然後重新載入頁面這個做法很方便。 您可以撰寫服務背景工作角色程式碼,以略過「等候」階段,並立即在更新時啟動,讓所有使用者自動執行此作業。 如果您略過等候階段,則會放棄一律從相同快取執行個體擷取資源的保證。
使用者可以執行應用程式的任何歷程記錄版本
Web 開發人員習慣預期使用者只會執行其 Web 應用程式的最新部署版本,因為這是傳統 Web 散發模型中的正常情況。 不過,離線優先 PWA 更類似於原生行動應用程式,其中使用者不一定是執行最新的版本。
如背景更新一節中所述,當您將更新部署至應用程式之後,每個現有使用者都會至少在下一次瀏覽時繼續使用舊版,因為更新會在背景中進行,且在使用者後續瀏覽之後才會啟動。 此外,所使用的舊版不一定是您先前部署的版本。 根據使用者上次完成更新的時間而定,舊版可以是任何歷程記錄版本。
如果您的應用程式前端和後端部分需要與 API 要求的結構描述達成協議,則這可能會是個問題。 您必須確定所有使用者都已升級後,才能部署回溯不相容的 API 結構描述變更。 或者,封鎖使用者使用不相容的舊版應用程式。 此案例需求與原生行動應用程式的需求相同。 如果您在伺服器 API 中部署中斷性變更,則尚未更新的使用者會中斷用戶端應用程式。
可能的話,請勿將中斷性變更部署到後端 API。 如果您必須這麼做,請考慮使用 ServiceWorkerRegistration 等標準服務背景工作角色 API 來判斷應用程式是否為最新狀態,如果不是,則避免使用。
干擾伺服器轉譯的頁面
如支援伺服器轉譯的頁面一節中所述,如果您想要略過服務背景工作角色傳回所有導覽要求 /index.html
內容的行為,請編輯服務背景工作角色中的邏輯。
快取所有服務背景工作角色資產資訊清單內容
如控制資產快取一節中所述,service-worker-assets.js
檔案會在建置期間產生,並列出服務背景工作角色應擷取及快取的所有資產。
由於此清單包含發出至 wwwroot
的所有項目,包括外部套件和專案提供的內容,因此您必須小心不要將太多內容放在該處。 如果 wwwroot
目錄包含數百萬個映像,服務背景工作角色就會嘗試擷取及快取這些映像、耗用過多的頻寬,而且很可能未順利完成。
實作任意邏輯,以控制應該透過編輯 service-worker.published.js
中的 onInstall
函式來擷取及快取資訊清單內容的子集。
與驗證的互動
PWA 範本可以搭配驗證使用。 具備離線功能的 PWA 也可以在使用者具有初始網路連線能力時支援驗證。
當使用者沒有網路連線能力時,就無法驗證或取得存取權杖。 嘗試在沒有網路存取時瀏覽登入頁面會導致「網路錯誤」訊息。 您必須設計 UI 流程,讓使用者在離線時執行有用的工作,而不嘗試驗證使用者或取得存取權杖。 或者,當網路無法使用時,您可以將應用程式設計為正常失敗。 如果無法設計應用程式來處理這些案例,您可能不需要啟用離線支援。
當專為線上和離線使用而設計的應用程式再次上線時:
- 應用程式可能需要佈建新的存取權杖。
- 應用程式必須偵測不同的使用者是否已登入服務,以便將作業套用至離線時所建立的使用者帳戶。
若要建立與驗證互動的離線 PWA 應用程式:
- 將 AccountClaimsPrincipalFactory<TAccount> 取代為儲存上次登入使用者的處理站,並在應用程式離線時,使用預存使用者。
- 應用程式離線時將作業加入佇列,以及在應用程式返回線上時套用加以作業。
- 在登出期間,清除預存使用者。
CarChecker
範例應用程式會示範上述方法。 請參閱應用程式的下列部分:
OfflineAccountClaimsPrincipalFactory
(Client/Data/OfflineAccountClaimsPrincipalFactory.cs
)LocalVehiclesStore
(Client/Data/LocalVehiclesStore.cs
)LoginStatus
元件 (Client/Shared/LoginStatus.razor
)