如何快速获取LocalStorage数据?优化Blazor大量数据读取性能
Blazor 3000+条LocalStorage数据加载与级联传递优化方案
1. 优化LocalStorage序列化与读取性能
大量数据的JSON序列化/反序列化是主要性能瓶颈之一,可通过以下方式优化:
- 改用
System.Text.Json并配置精简序列化规则,忽略空值、大小写不敏感匹配,减少序列化体积 - 对数据进行压缩存储,降低LocalStorage的读写IO开销
修改GetProducts方法及新增存储逻辑:
// 注入Blazored.LocalStorage的IJsonSerializer和ILocalStorageService private readonly IJsonSerializer _jsonSerializer; private readonly ILocalStorageService _localStorageService; public MainLayout(IJsonSerializer jsonSerializer, ILocalStorageService localStorageService) { _jsonSerializer = jsonSerializer; _localStorageService = localStorageService; } public async Task GetProducts() { // 读取压缩后的Base64字符串 var compressedData = await _localStorageService.GetItemAsStringAsync("ItemsCompressed"); if (!string.IsNullOrEmpty(compressedData)) { // 解压并反序列化 var bytes = Convert.FromBase64String(compressedData); using var decompressor = new GZipStream(new MemoryStream(bytes), CompressionMode.Decompress); using var reader = new StreamReader(decompressor); var json = await reader.ReadToEndAsync(); product = _jsonSerializer.Deserialize<List<Product>>(json, new JsonSerializerOptions { IgnoreNullValues = true, PropertyNameCaseInsensitive = true }); } } // 首次存储数据时执行压缩(示例) public async Task SaveProducts(List<Product> products) { var json = _jsonSerializer.Serialize(products, new JsonSerializerOptions { IgnoreNullValues = true }); using var compressor = new GZipStream(new MemoryStream(), CompressionMode.Compress); using var writer = new StreamWriter(compressor); await writer.WriteAsync(json); await writer.FlushAsync(); compressor.Position = 0; using var ms = new MemoryStream(); await compressor.CopyToAsync(ms); var compressedData = Convert.ToBase64String(ms.ToArray()); await _localStorageService.SetItemAsStringAsync("ItemsCompressed", compressedData); }
2. 优化级联值传递,减少变更检测开销
不要传递整个MainLayout实例,直接传递Product列表,并开启IsFixed(如果数据加载后无变更),Blazor会跳过该级联值的变更检测:
修改MainLayout.razor的级联值部分:
<div class="page"> <NavMenu/> <!-- 直接传递Product列表,而非组件实例 --> <CascadingValue Value="product" IsFixed="true"> <main class="margin-page container px-0"> <article class="px-1"> @Body </article> </main> </CascadingValue> </div>
子组件接收时直接注入:
[CascadingParameter] public List<Product> Products { get; set; }
3. 延迟加载,避免阻塞页面渲染
不在OnInitializedAsync中同步等待数据加载,先渲染页面框架,后台加载数据完成后再更新UI:
修改MainLayout代码:
<div class="page"> <NavMenu/> <CascadingValue Value="product" IsFixed="true"> <main class="margin-page container px-0"> @if (isLoading) { <div class="loading">加载中...</div> } else { <article class="px-1"> @Body </article> } </main> </CascadingValue> </div> @code { public List<Product> product { get; set; } = new List<Product>(); private bool isLoading = true; protected override async Task OnInitializedAsync() { // 后台异步加载,不阻塞页面渲染 _ = LoadProductsAsync(); await base.OnInitializedAsync(); } private async Task LoadProductsAsync() { await GetProducts(); isLoading = false; // 通知UI更新 StateHasChanged(); } // 原GetProducts方法保留 }
4. 内存缓存复用,避免重复读取LocalStorage
将加载后的Product列表存入全局Singleton缓存,后续页面切换无需重复读取LocalStorage:
创建缓存服务:
public class ProductCacheService { public List<Product> Products { get; private set; } = new List<Product>(); public bool IsLoaded { get; private set; } public async Task LoadFromLocalStorage(ILocalStorageService localStorageService) { if (!IsLoaded) { var result = await localStorageService.GetItem<List<Product>>("Items"); if (result != null) { Products = result; } IsLoaded = true; } } }
在Program.cs注册:
builder.Services.AddSingleton<ProductCacheService>();
修改MainLayout的加载逻辑:
private readonly ProductCacheService _productCache; public MainLayout(ProductCacheService productCache, ILocalStorageService localStorageService) { _productCache = productCache; _localStorageService = localStorageService; } private async Task LoadProductsAsync() { await _productCache.LoadFromLocalStorage(_localStorageService); product = _productCache.Products; isLoading = false; StateHasChanged(); }
5. 组件端按需渲染,减少DOM压力
如果子组件需要展示Product列表,使用Blazor内置的Virtualize组件实现虚拟滚动,只渲染可视区域内的条目:
子组件示例:
<Virtualize Items="@Products" Context="product" ItemSize="50"> <div class="product-item">@product.Name</div> </Virtualize> @code { [CascadingParameter] public List<Product> Products { get; set; } }
内容的提问来源于stack exchange,提问作者Haneen Al-fakhry
相关产品推荐
相关产品推荐

