Blazor WebAssembly集成ReactJS时blazorInterop未定义问题求助
问题背景
任务
现有一个C#编写的类库,需要让ReactJS调用其中的MdProcessor.ParseAndRender方法——传入字符串并获取返回的字符串结果。计划通过Blazor WebAssembly(BWASM)实现:将BWASM项目用dotnet publish发布后,在ReactJS中接入发布文件来调用目标函数。
现有实现代码
Blazor WebAssembly的Program.cs
using MarkdownProcessorWasm; using MdPWASM; using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using Microsoft.JSInterop; using System; using System.Threading.Tasks; using MarkdownProcessor; using MarkdownProcessor.Classes; using MarkdownProcessor.Interfaces; using MarkdownProcessor.Structs.Tags; var builder = WebAssemblyHostBuilder.CreateDefault(args); var host = builder.Build(); var jsRuntime = host.Services.GetRequiredService<IJSRuntime>(); await jsRuntime.InvokeVoidAsync("console.log", "Hello World from Blazor WebAssembly!"); var jsInterop = new MarkdownProcessorInterop(); await jsRuntime.InvokeVoidAsync("blazorInterop.registerProcessor", DotNetObjectReference.Create(jsInterop)); await host.RunAsync(); public class MarkdownProcessorInterop { private MdProcessor _markdownProcessor; public MarkdownProcessorInterop() { var ital = new ItalicsTag(); var bold = new BoldTag(); var link = new Link(); var header = new HeaderTag(); var main = new MainTag(); var list = new List<ITag> { bold, ital, link, header, main }; _markdownProcessor = new MdProcessor(new StringParser(list), new ConsoleMdRenderer()); } [JSInvokable] public string ParseMarkdown(string markdownText) { return _markdownProcessor.ParseAndRender(markdownText); } }
Blazor WebAssembly的index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>MdPWASM</title> <base href="/" /> <link rel="stylesheet" href="css/app.css" /> <!-- If you add any scoped CSS files, uncomment the following to load them <link href="MdPWASM.styles.css" rel="stylesheet" /> --> </head> <body> <script> window.blazorInterop = { registerProcessor: function (processor) { window.processor = processor; }, parseMarkdown: async function (markdownText) { if (!window.processor) { throw new Error("WASM module is not ready yet."); } return await window.processor.invokeMethodAsync("ParseMarkdown", markdownText); } }; </script> <script src="_framework/blazor.webassembly.js"></script> <script src="Interop.js"></script> </body> </html>
React客户端代码
useEffect(() => { const loadBlazor = async () => { try { await import('http://localhost:8080/_framework/blazor.webassembly.js'); await window.Blazor.start({ loadBootResource: (type, name, defaultUri, integrity) => { return `http://localhost:8080/_framework/${name}`; } }).then(() => { console.log('Blazor WebAssembly is initialized'); }).catch(error => { console.error('Blazor WebAssembly initialization error:', error); }); } catch (error) { console.error('Error while fetching Blazor WebAssembly:', error); } }; loadBlazor(); }, []); const processMarkdown = async () => { try { if (window.processor) { const parsedHtml = await window.blazorInterop.parseMarkdown(markdownText); setHtmlOutput(parsedHtml); } else { console.error('Blazor WebAssembly is not loaded'); } } catch (error) { console.error('Error while parsing Markdown:', error); } };
当前遇到的问题
通过localhost:8080获取发布文件,WebAssembly加载成功,Blazor的console.log能正常执行,但调用blazorInterop.registerProcessor时提示“blazorInterop未定义”。排查发现Blazor代码执行时,window上的blazorInterop属性还没创建;尝试延迟调用却陷入无限循环。需要解决两个核心问题:
- 如何确保blazorInterop正常声明?
- 如何将MdProcessor类库转为Wasm并保留ReactJS作为唯一前端框架?
解决方案建议
一、解决blazorInterop未定义的问题
问题核心是Blazor初始化时,blazorInterop还未挂载到window对象上,可通过以下两种方式修复:
1. 提前在React端声明blazorInterop
在React的Blazor加载逻辑中,先在window上定义blazorInterop的基础结构,再加载Blazor脚本,确保Blazor调用时该对象已存在:
useEffect(() => { const loadBlazor = async () => { try { // 提前初始化blazorInterop,避免Blazor调用时未定义 window.blazorInterop = { processor: null, registerProcessor: function(processor) { window.blazorInterop.processor = processor; }, parseMarkdown: async function(markdownText) { if (!window.blazorInterop.processor) { throw new Error("WASM模块尚未准备就绪"); } return await window.blazorInterop.processor.invokeMethodAsync("ParseMarkdown", markdownText); } }; // 加载Blazor脚本 await import('http://localhost:8080/_framework/blazor.webassembly.js'); await window.Blazor.start({ loadBootResource: (type, name, defaultUri, integrity) => { return `http://localhost:8080/_framework/${name}`; } }).then(() => { console.log('Blazor WebAssembly初始化完成'); }).catch(error => { console.error('Blazor WebAssembly初始化错误:', error); }); } catch (error) { console.error('加载Blazor WebAssembly出错:', error); } }; loadBlazor(); }, []);
同时删除Blazor index.html中的blazorInterop定义,避免重复或加载顺序冲突。
2. 利用Blazor初始化回调确保时机正确
Blazor提供了运行时就绪的回调机制,修改Program.cs,等待Blazor完全初始化后再执行JS互操作:
var builder = WebAssemblyHostBuilder.CreateDefault(args); var host = builder.Build(); var jsRuntime = host.Services.GetRequiredService<IJSRuntime>(); // 等待Blazor运行时就绪后再执行互操作逻辑 await jsRuntime.InvokeAsync<IJSObjectReference>("import", "_framework/blazor.webassembly.js") .ContinueWith(async _ => { await jsRuntime.InvokeVoidAsync("console.log", "Hello World from Blazor WebAssembly!"); var jsInterop = new MarkdownProcessorInterop(); await jsRuntime.InvokeVoidAsync("blazorInterop.registerProcessor", DotNetObjectReference.Create(jsInterop)); }); await host.RunAsync();
这种方式能确保Blazor环境完全准备好后,再去调用window上的blazorInterop方法。
二、将MdProcessor类库转为Wasm并保留React作为唯一前端
只需完成以下几步:
- 调整类库目标框架:将MdProcessor类库的目标框架改为
.NET 7或更高版本(与Blazor Wasm项目一致),确保代码能编译为Wasm兼容的字节码。 - 正确引用类库:在Blazor Wasm项目中添加对MdProcessor类库的项目引用或NuGet引用,确保发布时类库代码被打包到Wasm资源中。
- 移除Blazor UI依赖:因为仅用Blazor作为Wasm运行时,不需要UI组件,删除Blazor项目中的
App.razor、MainLayout.razor等UI文件,让Program.cs只负责初始化运行时和JS互操作逻辑。 - 发布与部署:执行
dotnet publish -c Release生成发布文件,将_framework目录部署到静态资源服务器(如localhost:8080),React端通过该地址加载Wasm资源即可。
内容的提问来源于stack exchange,提问作者GreenSL
相关产品推荐
相关产品推荐

