如何解决文件变更触发OnChanged时Blazor组件UI未同步更新问题
Blazor文件监听后组件不刷新修复方案
根本原因
FileSystemWatcher的事件回调运行在独立的后台线程,不在Blazor的UI同步上下文中,直接在回调里修改绑定的模型数据,不会触发组件的重渲染管线,所以数据更新后页面不会同步变化。把方法改成静态完全解决不了问题,反而会破坏实例参数的绑定逻辑。
具体修改点
- 所有修改绑定模型、触发UI更新的逻辑,必须放到
InvokeAsync()委托中执行,切回UI同步上下文,避免跨线程访问异常 - 模型数据更新完成后,手动调用
StateHasChanged()方法,显式通知组件重新渲染 - 组件实现
IDisposable接口,在销毁时注销文件监听器的事件订阅、释放监听器资源,避免内存泄漏 - 可选优化:给
Changed事件加防抖,因为文件写入时可能连续触发多次变更事件,避免不必要的重复渲染
修改后完整代码
@implements IDisposable <div class="orders"> <div class="sales"> <h3>Asks</h3> <table class="table text-light orders-table table-hover"> <thead> <tr class="text-center"> <th>Order Id</th> <th>Price</th> <th>Quantity</th> <th></th> </tr> </thead> <tbody> @foreach(var order in Model.Sales) { <tr> <td>@order.OrderID</td> <td>@order.Price.ToString("0.00")</td> <td>@order.Quantity</td> </tr> } </tbody> </table> </div> <div class="buys"> <h3>Bids</h3> <table class="table text-light orders-table table-hover"> <thead> <tr class="text-center"> <th>Order Id</th> <th>Price</th> <th>Quantity</th> <th></th> </tr> </thead> <tbody> @foreach(var order in Model.Buys) { <tr> <td>@order.OrderID</td> <td>@order.Price.ToString("0.00")</td> <td>@order.Quantity</td> </tr> } </tbody> </table> </div> </div> @code { // 全局Random实例避免短时间调用生成重复值 private readonly Random _random = new Random(); private readonly FileSystemWatcher watcher = new FileSystemWatcher(@"My\folder\directory\"); [Parameter] public IndexViewModel Model { get; set; } protected override void OnInitialized() { watcher.NotifyFilter = NotifyFilters.Attributes | NotifyFilters.CreationTime | NotifyFilters.DirectoryName | NotifyFilters.FileName | NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.Security | NotifyFilters.Size; watcher.Changed += OnChanged; watcher.Created += OnCreated; watcher.Deleted += OnDeleted; watcher.Renamed += OnRenamed; watcher.Filter = "*.txt"; watcher.IncludeSubdirectories = true; watcher.EnableRaisingEvents = true; } private void OnChanged(object sender, FileSystemEventArgs e) { // 切回UI线程执行数据更新和渲染操作 _ = InvokeAsync(() => { Model = new IndexViewModel(new List<OrderViewModel>(), new List<OrderViewModel>()); Console.WriteLine($"Changed: {e.FullPath}"); Model.Sales.Add(new OrderViewModel() { OrderID = _random.Next(int.MaxValue), Price = _random.Next(10), Quantity = _random.Next(100) }); Model.Sales = Model.Sales.OrderBy(x => x.Price).ToList(); Model.Buys.Add(new OrderViewModel() { OrderID = _random.Next(int.MaxValue), Price = _random.Next(20), Quantity = _random.Next(100) }); Model.Buys = Model.Buys.OrderByDescending(x => x.Price).ToList(); // 显式通知组件重渲染 StateHasChanged(); }); } private static void OnCreated(object sender, FileSystemEventArgs e) { Console.WriteLine($"Created: {e.FullPath}"); } private static void OnDeleted(object sender, FileSystemEventArgs e) => Console.WriteLine($"Deleted: {e.FullPath}"); private static void OnRenamed(object sender, RenamedEventArgs e) { Console.WriteLine($"Renamed:"); Console.WriteLine($" Old: {e.OldFullPath}"); Console.WriteLine($" New: {e.FullPath}"); } public void Dispose() { // 注销事件、释放资源避免内存泄漏 watcher.Changed -= OnChanged; watcher.Created -= OnCreated; watcher.Deleted -= OnDeleted; watcher.Renamed -= OnRenamed; watcher.Dispose(); } }
额外提示:原代码中表头的
Quantiy属于拼写错误,已修正为正确的Quantity。
内容的提问来源于stack exchange,提问作者Georgi Kolev
相关产品推荐
相关产品推荐

