Blazor中高效管理Javascript引用数组(IJSObjectReference)的方法
优化Blazor中JS对象数组引用的管理方案
针对你遇到的Blazor互操作中JS对象数组的引用管理问题,这里提供几种更高效的优化方案:
1. 批量预取元数据+引用映射,减少往返调用
一次性从JS端获取所有Foo的基础属性(id、name)和对应对象引用,避免逐个元素调用JS:
- JS侧代码:
function getFoosWithMetadata() { const foos = getAllFoos(); // 返回包含基础属性和对象引用的数组 return foos.map(foo => ({ id: foo.id, name: foo.name, ref: foo })); }
- .NET侧实现:
首先定义承载元数据和引用的类:
public class Foo { public int Id { get; } public string Name { get; } private readonly IJSObjectReference _jsRef; private readonly IJSRuntime _js; public Foo(int id, string name, IJSObjectReference jsRef, IJSRuntime js) { Id = id; Name = name; _jsRef = jsRef; _js = js; } // 封装JS方法调用 public async Task<string> DoSomething1() { return await _jsRef.InvokeAsync<string>("doSomething1"); } public async Task<int> DoSomething2() { return await _jsRef.InvokeAsync<int>("doSomething2"); } } // 对应JS返回结构的DTO类 public class FooMetadata { public int Id { get; set; } public string Name { get; set; } public IJSObjectReference Ref { get; set; } }
然后一次性获取所有数据:
@inject IJSRuntime Js // ... var foosMetadata = await Js.InvokeAsync<List<FooMetadata>>("getFoosWithMetadata"); var fooList = foosMetadata.Select(m => new Foo(m.Id, m.Name, m.Ref, Js)).ToList(); // 直接遍历基础属性,无需JS调用 foreach(var foo in fooList) { Console.WriteLine($"{foo.Id}: {foo.Name}"); } // 需要调用方法时直接使用封装好的方法 var result = await fooList[0].DoSomething1();
2. 封装通用JS数组包装器,支持批量操作
如果需要频繁对数组进行批量操作,可以在JS侧封装一个数组包装类,提供批量获取、批量调用方法的能力,减少跨边界调用次数:
- JS侧代码:
class ArrayWrapper { constructor(arr) { this.arr = arr; } // 批量获取指定索引范围的元数据 getBatchMetadata(startIdx, endIdx) { return this.arr.slice(startIdx, endIdx).map(item => ({ id: item.id, name: item.name })); } // 批量调用指定索引元素的方法 batchDoSomething1(indices) { return indices.map(idx => this.arr[idx].doSomething1()); } // 获取单个元素的引用 getItemRef(index) { return this.arr[index]; } } function wrapFoosArray() { return new ArrayWrapper(getAllFoos()); }
- .NET侧封装:
public class FooCollection { private readonly IJSObjectReference _wrapperRef; private readonly IJSRuntime _js; public FooCollection(IJSObjectReference wrapperRef, IJSRuntime js) { _wrapperRef = wrapperRef; _js = js; } // 批量获取元数据 public async Task<List<FooMetadata>> GetBatchMetadata(int startIdx, int endIdx) { return await _wrapperRef.InvokeAsync<List<FooMetadata>>("getBatchMetadata", startIdx, endIdx); } // 批量调用方法 public async Task<List<string>> BatchDoSomething1(List<int> indices) { return await _wrapperRef.InvokeAsync<List<string>>("batchDoSomething1", indices); } // 获取单个Foo实例 public async Task<Foo> GetItem(int index) { var metadataList = await _wrapperRef.InvokeAsync<List<FooMetadata>>("getBatchMetadata", index, index + 1); var jsRef = await _wrapperRef.InvokeAsync<IJSObjectReference>("getItemRef", index); return new Foo(metadataList[0].Id, metadataList[0].Name, jsRef, _js); } }
使用时只需创建一次包装器引用:
var wrapperRef = await Js.InvokeAsync<IJSObjectReference>("wrapFoosArray"); var fooCollection = new FooCollection(wrapperRef, Js); // 批量获取10-20号元素的元数据 var batchMetadata = await fooCollection.GetBatchMetadata(10, 21);
3. 懒加载引用(按需获取)
如果大部分场景只需要基础属性,仅少数情况需要调用方法,可以先只获取所有元数据,在需要调用方法时再通过id获取对应引用:
- JS侧新增方法:
function getFooById(id) { return getAllFoos().find(foo => foo.id === id); } function getAllFoosMetadata() { return getAllFoos().map(foo => ({ id: foo.id, name: foo.name })); }
- .NET侧实现:
public class FooLite { public int Id { get; } public string Name { get; } private readonly IJSRuntime _js; public FooLite(int id, string name, IJSRuntime js) { Id = id; Name = name; _js = js; } // 按需获取完整Foo实例 public async Task<Foo> GetFullFoo() { var jsRef = await _js.InvokeAsync<IJSObjectReference>("getFooById", Id); return new Foo(Id, Name, jsRef, _js); } } // 先获取所有轻量实例 var fooLites = await Js.InvokeAsync<List<FooLite>>("getAllFoosMetadata"); // 需要调用方法时再获取完整引用 var fullFoo = await fooLites.First(f => f.Id == 5).GetFullFoo(); var result = await fullFoo.DoSomething1();
内容的提问来源于stack exchange,提问作者Luke Vo
相关产品推荐
相关产品推荐

