【问题标题】:JsInterrop module in WebAssemblyWebAssembly 中的 JsInterrop 模块
【发布时间】:2022-09-28 05:26:17
【问题描述】:

我正在使用异步 IJSRuntime 在我的 WASM 项目中运行 JsInterrop 函数,并使用 JS 模块来加载该函数。

    private readonly Lazy<Task<IJSObjectReference>> moduleTask;
    public LocalJsInterop(IJSRuntime jsRuntime)
    {
      moduleTask = new(() => jsRuntime.InvokeAsync<IJSObjectReference>(
          \"import\", \"./../_content/WebPlayerControls/localJsInterop.js\").AsTask());
    }

然后像这样使用它来调用一个函数

    public async ValueTask<string> GetWindowLocation()
    {
      var module = await moduleTask.Value;

      string value = await module.InvokeAsync<string>(\"GetWindowLocation\");

      return value;
    }

我最近刚刚看到我可以使用 IJSInProcessRuntime 来同步运行该函数并尝试更改我的函数以使用它。

但我无法让我的模块工作。我试着像这样初始化它

module = jsRuntime.Invoke<IJSObjectReference>(\"import\", \"./../_content/WebPlayerControls/localJsInterop.js\");

但是它不会提供对同步 Invoke<> 的访问权限,而只能访问异步 InvokeAsync<>

我可能可以将这些函数放在 index.html 中以使用它们,但我想知道我是否仍然可以使用该模块。有人知道如何使导入模块以同步方式工作吗?

编辑

我尝试像这样使用 IJSInProcessObjectReference

    private readonly Lazy<IJSInProcessObjectReference> moduleTask;

    public LocalJsInterop(IJSInProcessRuntime jsRuntime)
    {
      moduleTask = new(() => jsRuntime.Invoke<IJSInProcessObjectReference>(\"import\", \"./../_content/WebPlayerControls/localJsInterop.js\"));
      m_jsRuntime = jsRuntime;
    }

    public void ShowMessage()
    {
      var module = moduleTask.Value;

      module.InvokeVoid(\"ShowMessage\");
    }

但现在代码执行卡在 InvokeVoid 行。如果我尝试使用 InvokeVoidAsync,它会给出未找到函数的异常

  • 您是否尝试过使用 IJSInProcessObjectReference 代替?
  • @MisterMagoo 我尝试使用但它不起作用,使用 Invoke 代码执行被卡住而没有抛出错误,而使用 InvokeAsync 它抛出一个错误,说找不到 js 函数
  • 请使用您尝试过的完整代码更新您的问题

标签: blazor blazor-webassembly blazor-jsinterop


【解决方案1】:

您尝试使用同步方法时存在一些问题。有很多关于实现 JS Interop here 的各种方法的好信息。但是,它肯定缺少一些关键信息。

IJS运行时

首先,你不能注入IJSInProcessRuntime。只有基本接口是可注入的 (IJSRuntime)。如果您想使用 Runtime 接口而不处理 ObjectReference,那么您将不得不向下转换注入的对象。例如:

public LocalJsInterop(IJSRuntime jsRuntime)
{
    ((IJSInProcessRuntime)jsRuntime).InvokeVoid<string>("displayAlert", "Hello, World!");
}

您可能已经知道,为了直接从 IJSRuntime 调用 JS 函数,它们必须在全局 Window 接口上声明。继续上面的例子,看起来像这样:

window.displayAlert = function(message) {
    alert(message);
}

IJS对象参考

其次,你不能同步导入IJSObjectReference,不幸的是也不能注入它。如果您想使用 ObjectReference 接口(根据您的问题,它看起来像您所做的那样),那么您将必须异步导入对象引用。例如:

private IJSRuntime _runtime;
public LocalJsInterop(IJSRuntime jsRuntime)
{
    _runtime = jsRuntime;
}

public async Task RunJS() {
    // Option 1: Using object reference asynchronously
    using (var jsObjRef = await _runtime.InvokeAsync<IJSObjectReference>("import", "<relative_path_to_js_module>"))
    {
        await jsObjRef.InvokeVoidAsync("displayAlert", "Hello, World!");
    }

    // Option 2: Using object reference synchronously
    using (var jsObjRef = await _runtime.InvokeAsync<IJSInProcessObjectReference>("import", "<relative_path_to_js_module>"))
    {
        jsObjRef.InvokeVoid("displayAlert", "Hello, World!");
    }
}

您可能知道,使用IJSObjectReference 接口允许您调用隔离的JS/ES 模块,因此您不必将所有内容都声明为全局Window 接口的一部分。例如:

export function displayAlert(message) {
    alert(message);
}

重要的最后说明:请注意IJSObjectReference 示例中的using 语句。该接口实现了IAsyncDisposable,并且应该在您完成它时将其包装在usings 中或者如果您将其存储在一个类变量中以供多次使用,那么您的类应该继承自IAsyncDisposable并实现 dispose 方法。例如:

public async ValueTask DisposeAsync()
{
    GC.SuppressFinalize(this);
    if (JsObjectReference != null) await JsObjectReference.DisposeAsync();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-12-16
    • 2019-03-11
    • 1970-01-01
    • 1970-01-01
    • 2020-05-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多