javascript - JSInterop kompilert med webpack er udefinert i blazor


Jeg har en Blazor WebAssembly-applikasjon, der jeg prøver å inkludere TypeScript/JavaScript-kode ved bruk av JSInterop, som feiler når JS-koden blir laget av webpack under bygging.

Når jeg oppretter en enkel hello-world.js-fil i min wwwroot med følgende innhold:

export function showAlertSimple() {
    alert("hello world");
}

Jeg kan påkalle det veldig enkelt ved å bruke:

public class HelloWorld : JSModule
{
    public HelloWorld(IJSRuntime js) 
        : base(js, "./js/hello-world.js")
    {
    }

    public async Task ShowAlert() => await InvokeVoidAsync("showAlert");
}

Hvis jeg prøver å gjøre det samme med en typescript-fil, som jeg opprettet et annet sted og deretter lar den kompilere ned til ES6 med webpack, og setter den i samme mappe, klarer ikke Blazor å kalle metoden:

./NpmJs/src/hello-world2.ts

export function showAlertFromWebPack() {
    alert("Hello World from webpack");
}

./NpmJs/webpack.config.js

module.exports = {
    entry: {
        helloworld2: './src/hello-world2.ts',
    },
    module: {
        rules: [
            {
                test: /\.ts?$/,
                use: 'ts-loader',
                exclude: /node_modules/,
            },
        ],
    },
    resolve: {
        extensions: ['.tsx', '.ts', '.js'],
    },
    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, '../../wwwroot/js'),
    },
    devtool: 'source-map'
};
public class HelloWorld2 : JSModule
{
    public HelloWorld2(IJSRuntime js)
        : base(js, "./js/helloworld2.bundle.js")
    {
    }

    public async Task ShowAlert() => await InvokeVoidAsync("showAlertFromWebPack");
}

Dette kaster da følgende unntak i nettleserkonsollen:

crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
      Unhandled exception rendering component: Could not find 'showAlertFromWebPack' ('showAlertFromWebPack' was undefined).
      Error: Could not find 'showAlertFromWebPack' ('showAlertFromWebPack' was undefined).

Filene i seg selv lastes korrekt. Jeg kan se den i kildefanen.

Hva går jeg glipp av her?

For fullstendighets skyld, her er JSModule-klassen, som jeg skamløst har tatt fra Steven Sanderson på github.(https://github.com/SteveSandersonMS/AudioBrowser/blob/dd7a03d93b4de5e97efb333f1120792ee48c70ba/MediaFilesAPI/Util/JSModule.cs#L7)

public abstract class JSModule : IAsyncDisposable
{
    private readonly Task<IJSObjectReference> moduleTask;

    // On construction, we start loading the JS module
    protected JSModule(IJSRuntime js, string moduleUrl)
        => moduleTask = js.InvokeAsync<IJSObjectReference>("import", moduleUrl).AsTask();

    // Methods for invoking exports from the module
    protected async ValueTask InvokeVoidAsync(string identifier, params object[]? args)
        => await (await moduleTask).InvokeVoidAsync(identifier, args);
    protected async ValueTask<T> InvokeAsync<T>(string identifier, params object[]? args)
        => await (await moduleTask).InvokeAsync<T>(identifier, args);

    // On disposal, we release the JS module
    public async ValueTask DisposeAsync()
        => await (await moduleTask).DisposeAsync();
}

Svar #1

Jeg fant 2 fungerende løsninger. Den ene bruker webpack, den andre ved å kvitte seg med webpack og erstatte det med vitejs.

Ved å bruke webpack må vi aktivere experiments.outputModule (https://webpack.js.org/configuration/experiments/#experimentsoutputmodule) og sette output.library.type til module (https://webpack.js.org/configuration/output/#outputlibrarytype):

Den endelige konfigurasjonen vil da se slik ut:

module.exports = {
    entry: {
        helloworld2: './src/hello-world2.ts',
    },
    module: {
        rules: [
            {
                test: /\.ts?$/,
                use: 'ts-loader',
                exclude: /node_modules/,
            },
        ],
    },
    resolve: {
        extensions: ['.tsx', '.ts', '.js'],
    },
    output: {
        filename: '[name].bundle.js',
        path: path.resolve(__dirname, '../../wwwroot/js'),
        library: {
            type: "module",
        },
    },
    experiments: {
       outputModule: true
    },
    devtool: 'source-map'
};

Ved bruk av vitejs i vite.config.js, vil konfigurasjonen se slik ut:

import * as path from 'path';
import { defineConfig } from "vite";

export default defineConfig({
   appType: 'mpa',
   build: {
      target: 'esnext',
      outDir: '../wwwroot/js',
      lib: {
         entry: path.resolve(__dirname, './src/index.ts'),
         name: "YourPackageName",
         fileName: (format) => `your-package-name.${format}.js`,
      },
   }
});

Vite vil da selv produsere 2 filer:

  • your-package-name.es.js
  • your-package-name.umd.js

Du må koble til/bruke es-filen inne i Blazor, så er du klar til å gå.

Relaterte artikler