You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决文件变更触发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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 02:36:50