.NET 是一个开源、跨平台的开发平台,运行稳定,资源消耗低,AOT 编译进一步降低了交付体积。本文基于 .NET 10 实现一个零配置的热重载服务器,核心代码不到 50 行。
依赖安装:
dotnet add package PicoServer
一、热重载的本质
热重载解决的问题:文件保存后浏览器自动刷新。
传统流程:修改 → 保存 → 切换窗口 → F5(4 步)
热重载后:修改 → 保存(2 步)
核心流程:
文件变更 → 服务端检测 → WebSocket 推送 → 浏览器刷新
二、实现
2.1 文件监控与防抖
csharp
using PicoServer;
using System.Timers;
using System.IO;
string staticRoot = "wwwroot";
var app = new WebAPIServer();
var watcher = new FileSystemWatcher(staticRoot)
{
EnableRaisingEvents = true,
IncludeSubdirectories = true
};
var timer = new System.Timers.Timer(200) { AutoReset = false };
timer.Elapsed += async (_, _) => await app.WsBroadcastAsync("reload");
void OnChange(object s, FileSystemEventArgs e)
{
var name = Path.GetFileName(e.Name);
if (string.IsNullOrEmpty(name) || name.StartsWith('.') || name.StartsWith('~'))
return; // 过滤 IDE 临时文件
timer.Stop();
timer.Start(); // 200ms 内无新事件才触发
}
watcher.Changed += OnChange;
watcher.Created += OnChange;
watcher.Deleted += OnChange;
watcher.Renamed += OnChange;
防抖的必要性:一次保存操作可能触发 Changed、Created、Renamed 多个事件,防抖将其合并为一次广播,避免页面重复刷新。
2.2 WebSocket 广播
csharp
app.enableWebSocket = true;
PicoServer 开启 WebSocket 后,通过以下方法广播:
csharp
await app.WsBroadcastAsync("reload");
2.3 客户端脚本注入
在返回 HTML 时自动注入 WebSocket 客户端代码,开发者无需手动添加:
csharp
if (filePath.EndsWith(".html", StringComparison.OrdinalIgnoreCase))
{
var content = await File.ReadAllTextAsync(filePath);
var script = """
<script>
new WebSocket('ws://'+location.host+'/ws-reload').onmessage = e => {
if (e.data === 'reload') location.reload();
};
</script>
""";
content = content.Contains("</body>")
? content.Replace("</body>", script + "</body>")
: content + script;
await res.WriteAsync(content, "text/html");
}
2.4 路径放行
/ws-reload 路径需要交给 WebSocket 处理,静态文件中间件应放行:
csharp
if (path == "/ws-reload") return true;
三、完整示例
csharp
using PicoServer;
using System.Timers;
var app = new WebAPIServer();
app.enableWebSocket = true;
var watcher = new FileSystemWatcher("wwwroot") { EnableRaisingEvents = true };
var timer = new System.Timers.Timer(200) { AutoReset = false };
timer.Elapsed += async (_, _) => await app.WsBroadcastAsync("reload");
watcher.Changed += (s, e) => { timer.Stop(); timer.Start(); };
app.AddMiddleware(async (req, res) =>
{
var path = req.Url?.AbsolutePath ?? "";
if (path == "/ws-reload") return true;
// 静态文件处理与脚本注入逻辑(见 2.3 节)
return false;
});
app.StartServer(8080);
Console.WriteLine("热重载已启动,按 Ctrl+C 停止");
await Task.Delay(Timeout.Infinite);
四、扩展方向
- CSS 热更新 :广播
css消息,客户端仅刷新样式链接 - 多设备同步:局域网内多端同时刷新
- 差异化刷新 :根据文件类型决定刷新策略(如
.css增量更新,.cshtml全页刷新)
五、关于 AOT
热重载工具本身也应当保持轻量。Node.js 工具链通常需要安装运行时并下载数百 MB 依赖。
通过 .NET AOT 编译,整个热重载服务器可打包为 4-10 MB 的单文件,无需安装 .NET 运行时,拷贝即用。
六、总结
| 组件 | 代码量 |
|---|---|
| 文件监控 + 防抖 | 15 行 |
| WebSocket 广播 | 1 行 |
| 脚本注入 | 10 行 |
| 总计 | 26 行 |
26 行代码,解决开发中最频繁的中断问题。
附:本文的热重载实现是 PicoSite 静态网站生成器的一部分。
如果你对 .NET 工具链开发感兴趣,或希望参与构建轻量级的静态网站生成方案,欢迎关注 PicoSite 开源项目。
.NET 开源平台:https://dotnet.microsoft.com/zh-cn/
Markdig 解析:https://github.com/xoofx/markdig
DotLiquid 模板:https://www.dotliquid.org/
PicoServer 托管:https://picoserver.cn