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

Blazor WebAssembly中如何缓存带数据的Razor页面

解决方案:Blazor WebAssembly 页面状态保留方案

针对你需要在页面切换后保留搜索条件、未提交编辑内容的需求,以下是几种适配Blazor WebAssembly的可行方案,按通用性和实用性排序:

1. 组件级状态容器(推荐用于多页面通用场景)

核心思路是把页面的搜索条件、编辑中的表单数据抽离到全局或局部状态容器中,而非页面组件内部。路由切换时页面组件会被销毁,但状态仍保存在容器里,返回页面时直接从容器读取即可。

实现步骤:

  • 创建通用状态容器类,支持多页面状态存储:
    public class PageStateContainer
    {
        private readonly Dictionary<string, object> _pageStates = new();
    
        public void SaveState<T>(string pageKey, T state)
        {
            _pageStates[pageKey] = state;
        }
    
        public T? GetState<T>(string pageKey)
        {
            _pageStates.TryGetValue(pageKey, out var state);
            return state is T typedState ? typedState : default;
        }
    
        public void ClearState(string pageKey)
        {
            _pageStates.Remove(pageKey);
        }
    }
    
  • 在Program.cs中注册为单例:
    builder.Services.AddSingleton<PageStateContainer>();
    
  • 在目标页面组件中注入并使用:
    @inject PageStateContainer StateContainer
    @implements IDisposable
    
    <!-- 搜索区域 -->
    <input @bind="SearchModel.Country" placeholder="国家" />
    <button @onclick="SearchCustomers">搜索</button>
    
    <!-- 客户编辑区域 -->
    @if (SelectedCustomer != null)
    {
        <input @bind="SelectedCustomer.Name" />
        <input @bind="SelectedCustomer.Email" />
    }
    
    @code {
        private const string PageKey = "CustomerListPage";
        private CustomerSearchModel SearchModel { get; set; } = new();
        private Customer? SelectedCustomer { get; set; }
        private List<Customer> Customers { get; set; } = new();
    
        protected override void OnInitialized()
        {
            // 初始化时读取缓存状态
            var cachedState = StateContainer.GetState<CustomerPageState>(PageKey);
            if (cachedState != null)
            {
                SearchModel = cachedState.SearchModel;
                SelectedCustomer = cachedState.SelectedCustomer;
                _ = SearchCustomers(); // 自动恢复搜索结果
            }
        }
    
        private async Task SearchCustomers()
        {
            Customers = await CustomerService.GetCustomersByCountry(SearchModel.Country);
            SaveCurrentState();
        }
    
        private void OnCustomerSelected(Customer customer)
        {
            SelectedCustomer = customer;
            SaveCurrentState();
        }
    
        private void SaveCurrentState()
        {
            StateContainer.SaveState(PageKey, new CustomerPageState
            {
                SearchModel = SearchModel,
                SelectedCustomer = SelectedCustomer
            });
        }
    
        public void Dispose()
        {
            // 若需关闭页面后清除状态,取消下方注释
            // StateContainer.ClearState(PageKey);
        }
    
        // 页面状态模型
        private class CustomerPageState
        {
            public CustomerSearchModel SearchModel { get; set; } = new();
            public Customer? SelectedCustomer { get; set; }
        }
    }
    
  • 优势:一套逻辑适配所有需要缓存的页面,状态集中管理,可扩展过期机制、持久化到本地存储等功能。

2. 使用KeepAlive组件(适合单页/少量页面)

Blazor的KeepAlive组件可让路由切换时不销毁页面组件,直接保留组件内部所有状态。

实现步骤:

  • 全局生效:在App.razor的路由中包裹KeepAlive:
    <Router AppAssembly="@typeof(App).Assembly">
        <Found Context="routeData">
            <KeepAlive>
                <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
            </KeepAlive>
        </Found>
        <NotFound>
            <LayoutView Layout="@typeof(MainLayout)">
                <p role="alert">Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
    
  • 单页生效:在目标页面组件顶部添加特性:
    @attribute [KeepAlive]
    @page "/customers"
    
    <!-- 页面内容 -->
    
  • 注意:组件会长期驻留内存,大量使用可能导致内存占用过高,适合核心页面而非全量页面;可通过OnActivated/OnDeactivated生命周期方法处理页面激活/失活逻辑。

3. 持久化到localStorage(适合跨会话保留场景)

把状态序列化后存储到浏览器localStorage,即使刷新页面或重新打开浏览器也能恢复状态。

实现步骤:

  • 封装本地存储服务:
    public class LocalStorageService
    {
        private readonly IJSRuntime _jsRuntime;
    
        public LocalStorageService(IJSRuntime jsRuntime)
        {
            _jsRuntime = jsRuntime;
        }
    
        public async Task SaveStateAsync<T>(string key, T state)
        {
            var json = JsonSerializer.Serialize(state);
            await _jsRuntime.InvokeVoidAsync("localStorage.setItem", key, json);
        }
    
        public async Task<T?> GetStateAsync<T>(string key)
        {
            var json = await _jsRuntime.InvokeAsync<string>("localStorage.getItem", key);
            return string.IsNullOrEmpty(json) ? default : JsonSerializer.Deserialize<T>(json);
        }
    
        public async Task ClearStateAsync(string key)
        {
            await _jsRuntime.InvokeVoidAsync("localStorage.removeItem", key);
        }
    }
    
  • 注册为单例后,在页面中替换状态容器的使用逻辑即可。
  • 优势:跨会话保留状态;劣势:序列化有性能开销,敏感数据不适合存储在此。

方案选择建议

  • 大部分页面需要状态保留:优先用组件级状态容器,统一管理且灵活可控。
  • 少数核心页面:用KeepAlive最简便,注意内存占用。
  • 需要跨会话恢复:结合状态容器和localStorage,同时在内存和本地存储中保存状态。

内容的提问来源于stack exchange,提问作者Nick Lazidis

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 18:30:28