在 .NET 8 Blazor Web App 中攔截所有內部導航並平滑捲動到頂端
Copyright Notice: This article is an original work licensed under the CC 4.0 BY-NC-ND license.
If you wish to repost this article, please include the original source link and this copyright notice.
Source link: https://v2know.com/article/1140
在 .NET 8 的 Blazor Web App(Hybrid 模式)中,當你在文章內容裡大量使用原生 <a href="…">
連結時,點擊後往往只會做「局部刷新」,導致頁面內容更新但瀏覽器捲軸位置依然停留在原地,使用者體驗大打折扣。本文將示範如何在宿主頁面(App.razor
)中,注入一段小小的 JavaScript 補丁,攔截所有內部路由並統一「平滑捲動到頂端」。
1. 背景與需求
-
局部刷新 vs 全頁刷新
Blazor Server/WASM 的 SPA 路由會攔截同源連結,透過 History API 做導航並只重渲染@Body
區域,元件雖然更新了,但捲軸位置不會歸零。 -
大量原生
<a>
博客文章裡常常直接寫:<a href="/category/技術分享">技術分享</a>
不想改成
<NavLink>
、也不想在每個連結上額外寫 JS。 -
目標
無論點擊<a>
、<NavLink>
、呼叫NavigateTo
,或是瀏覽器前進後退,都能在新內容渲染完畢後,一次性「平滑捲動到頁首」。
2. .NET 8 Hybrid 模式下的宿主頁面
在 .NET 6/7 的 Blazor Server,我們常把起始設在 _Host.cshtml
:
<body>
<component type="typeof(App)" render-mode="InteractiveServer" />
</body>
而在 .NET 8 的「Blazor Web App(Hybrid)」裡,App.razor
就相當於舊版的 _Host.cshtml
,同時支援 Server 與 WASM 的啟動:
<!DOCTYPE html>
<html lang="en">
<head> … </head>
<body>
<Routes />
<script src="_framework/blazor.web.js"></script>
</body>
</html>
因此,要做「全局攔截」或「最早注入」的腳本,就應該放在這個檔案裡。
3. 原理:攔截 History API 並觸發自定義事件
瀏覽器的 SPA 導航主要靠 history.pushState
(以及 popstate
):
-
Blazor 客戶端路由會呼叫
history.pushState(...)
-
使用者點擊瀏覽器「前進/後退」會觸發
popstate
我們可以「打補丁」(monkey-patch)它:
// 保留原始方法
const _pushState = history.pushState;
// 重寫 pushState
history.pushState = function () {
_pushState.apply(this, arguments);
window.dispatchEvent(new Event('spa-navigate'));
};
// 攔截前進/後退
window.addEventListener('popstate', () => {
window.dispatchEvent(new Event('spa-navigate'));
});
// 統一平滑捲動
window.addEventListener('spa-navigate', () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
每當發生內部導航或使用者前進/後退,就會觸發自定義的 spa-navigate
事件,接著執行 scrollTo
。
4. 在 App.razor 中注入補丁
將上述腳本,加入到你的 App.razor
(即 .NET 8 Hybrid 的宿主)中,示例如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link rel="stylesheet" href="v2knowBlazor.styles.css" />
<HeadOutlet />
</head>
<body>
<!-- Blazor 根組件佔位 -->
<Routes />
<!-- —— 平滑捲動補丁開始 —— -->
<script>
(function () {
const _pushState = history.pushState;
history.pushState = function () {
_pushState.apply(this, arguments);
window.dispatchEvent(new Event('spa-navigate'));
};
window.addEventListener('popstate', () => {
window.dispatchEvent(new Event('spa-navigate'));
});
window.addEventListener('spa-navigate', () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
});
})();
</script>
<!-- —— 平滑捲動補丁結束 —— -->
<script src="_framework/blazor.web.js"></script>
<script src="js/MatrixBackground.js"></script>
<script src="js/visitorDataInterop.js"></script>
<script src="js/sidebarInterop.js"></script>
<script src="js/articlePageHelper.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"
onerror="this.onerror=null;this.src='js/highlight.min.js';"></script>
</body>
</html>
注意:
補丁腳本必須放在
<Routes />
之後、Blazor 框架腳本之前,確保能攔截最早的路由呼叫。不要把這段腳本放到任何
.razor
組件裡,否則插入時機都太晚,無法全局攔截。
5. 完整示例
假設你的專案結構如下:
/Pages
└ App.razor ← .NET 8 Hybrid 的宿主
/wwwroot
/js
└ articlePageHelper.js
/css
└ Global.css
那麼最終的 Pages/App.razor
內容就是上述範例。此後,無論你在文章裡使用多少個純 <a href="/category/xxx">…</a>
,點擊都會觸發 spa-navigate
,並在新內容渲染完畢後平滑捲動到頂端。
6. 小結
-
.NET 8 Hybrid:
App.razor
就是宿主頁面,替代了過去的_Host.cshtml
/index.html
。 -
JavaScript 補丁:在宿主頁面打補丁攔截
history.pushState
+popstate
,再統一平滑捲動;不用修改任何組件或連結。 -
兼容性佳:同時支援純 HTML 連結、
<NavLink>
、NavigateTo()
、前進/後退,讓使用者在每次路由切換後都能自動回到頁首。
透過這段「秒級」的腳本補丁,你的博客內部連結再也不用一個個改寫成 <NavLink>
,用戶點完任何內部導航後,都能順暢地回到頂端,提升閱讀體驗。
This article was last edited at