本文來自轉載
原文作者:Token
原文標題:如何取消 Blazor Server 煩人的重新連線?
原文連結:https://www.cnblogs.com/hejiale010426/p/17498629.html
相信很多 Blazor 的使用者在開發內部系統時基本上都選擇速度更快、載入更快的 Blazor Server 模式。
但是 Blazor Server 因為是 SignalR 實作,所以在存取時會建立 WebSocket 通道,用於 js 互動和介面渲染,但由於 WebSocket 是長連線,這樣就會導致使用者在介面時會一直建立連線,造成伺服器頻寬佔用,所以微軟預設會在無操作的情況下自動斷開連線,然後會加上那個惱人的重新連線 UI,很難看,導致很多使用者看到灰色的效果。當然,微軟也提供了處理這種情況的方案,下面我們會使用微軟提供的方案解決這個問題。
建立 Blazor Server 的空專案
下面是剛建立的 Blazor Server 空專案。

然後長時間掛著就會出現下面這種情況。

很醜的載入 UI,灰色的全面覆蓋樣式,下面就必須解決這個問題。
移除灰色載入樣式
新增 boot.js JS 腳本,用來處理重新連線。
boot.js 用於自訂重新連線的操作,並接管重新連線的程序。
(() => {
// 重試次數
const maximumRetryCount = 10000;
// 重試間隔
const retryIntervalMilliseconds = 1000;
const startReconnectionProcess = () => {
let isCanceled = false;
(async () => {
for (let i = 0; i < maximumRetryCount; i++) {
console.log(`試圖重新連線: ${i + 1} of ${maximumRetryCount}`);
await new Promise((resolve) =>
setTimeout(resolve, retryIntervalMilliseconds)
);
if (isCanceled) {
return;
}
try {
const result = await Blazor.reconnect();
if (!result) {
// 已到達伺服器,但連線被拒絕;重新載入頁面。
location.reload();
return;
}
// 成功重新連線到伺服器。
return;
} catch {
// 沒有到達伺服器;再試一次。
}
}
// 重試次數過多;重新載入頁面。
location.reload();
})();
return {
cancel: () => {
isCanceled = true;
},
};
};
let currentReconnectionProcess = null;
Blazor.start({
configureSignalR: function (builder) {
let c = builder.build();
c.serverTimeoutInMilliseconds = 30000;
c.keepAliveIntervalInMilliseconds = 15000;
builder.build = () => {
return c;
};
},
reconnectionHandler: {
onConnectionDown: () =>
(currentReconnectionProcess ??= startReconnectionProcess()),
onConnectionUp: () => {
currentReconnectionProcess?.cancel();
currentReconnectionProcess = null;
},
},
});
})();
修改 Pages/_Host.cshtml
@page "/" @using Microsoft.AspNetCore.Components.Web @namespace BlazorApp1.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<!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="css/bootstrap/bootstrap.min.css" />
<link href="css/site.css" rel="stylesheet" />
<link href="BlazorApp1.styles.css" rel="stylesheet" />
<link rel="icon" type="image/png" href="favicon.png" />
<component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
</head>
<body>
<component type="typeof(App)" render-mode="ServerPrerendered" />
<div id="blazor-error-ui">
<environment include="Staging,Production">
發生錯誤。此應用程式在重新載入之前可能不再回應。
</environment>
<environment include="Development">
發生了一個未處理的例外。詳細資訊請參見瀏覽器開發工具。
</environment>
<a href="" class="reload">重新載入</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.server.js" autostart="false"></script>
<script src="/boot.js"></script>
</body>
</html>
我們修改了 blazor.server.js 的引用。
由 <script src="_framework/blazor.server.js"></script> 改成 <script src="_framework/blazor.server.js" autostart="false"></script>
說明一下為什麼要改這個。我們需要自訂處理程式,但引用了 blazor.server.js,它就會直接執行,並不會等我們的處理程式,所以需要加上 autostart="false" 阻止預設的啟動程式。
然後在下面的腳本中自訂啟動程式,在上面還引用了 <script src="/boot.js"></script>,然後下面的自訂處理啟動 Blazor Server 程式。
<script>
Blazor.start({
configureSignalR: function (builder) {
let c = builder.build();
c.serverTimeoutInMilliseconds = 30000;
c.keepAliveIntervalInMilliseconds = 15000;
builder.build = () => {
return c;
};
}
});
</script>
serverTimeoutInMilliseconds:伺服器逾時(以毫秒為單位)。 如果此逾時已過但未從伺服器接收任何訊息,連線將終止並出現錯誤。 預設逾時值為 30 秒。 伺服器逾時應至少是分配給 Keep-Alive 間隔 (keepAliveIntervalInMilliseconds) 的值的兩倍。keepAliveIntervalInMilliseconds:ping 伺服器時採用的預設間隔。 使用此設定,伺服器可以偵測強行斷開連線的情況,例如用戶端中斷其電腦的網路連線。 此 ping 的發生頻率最多與伺服器 ping 的頻率一樣。 如果伺服器每 5 秒 ping 一次,則分配的值低於5000(5 秒)時,將會每 5 秒 ping 一次。 預設值為 15 秒。 Keep-Alive 間隔應小於或等於分配給伺服器逾時 (serverTimeoutInMilliseconds) 的值的一半。
寫完以後啟動程式,然後將後端服務關閉,然後我們的介面並未再出現上面的 UI 了。

當我們的後端啟動就會與前端自動連線,並且不會對前端造成影響。
結尾
來自 token 的分享
Blazor 技術交流群:452761192