为什么 webpack 无法加载块?



我正在尝试在导入emscripten创建的webassembly库的Typescript项目上使用webpack。这一切都在 Power BI 自定义视觉对象中运行,这使一切变得更加有趣。我认为由于Power BI上下文,Typescript和WebAssembly,这特别令人毛骨悚然,但目前似乎它可能只是一个webpack问题。

我有一个我认为是路径问题的问题,但我是 webpack 的新手,有点迷茫。编译有效,但视觉对象为块 1 抛出"ChunkLoadError"。(有两个块。可能相关也可能不相关的内容:

  • 我可以将浏览器定向到"https://localhost:8080/assets/1.js"(或...0.js(很好。
  • 我发现(我认为(因为 Power BI 将我的代码放在 iframe 中,最初 webpack 尝试的请求 URL 是"https://app.powerbi.com/13.0.11428.218/assets/0.js"。它生成的脚本标记上有一个 baseUri 属性,导致这种情况。将output[publicPath]更改为"https://localhost:8080/assets/"允许浏览器下载块(我可以看到它们在浏览器的开发工具中成功出现(,但 webpack 仍然抱怨。
  • 已尝试吹走node_modules并重新安装。没有骰子。

在上述之后,错误如下所示:

VM1292:119 Uncaught (in promise) ChunkLoadError: Loading chunk 1 failed.
(missing: https://localhost:8080/assets/1.js)
    at Function.requireEnsure [as e] (<anonymous>:119:26)
    at new Visual (<anonymous>:23155:79)
    at r.create [as creator] (<anonymous>:237:14)
    at r.init (https://app.powerbi.com/13.0.11499.187/scripts/customVisualsHost.bundle.min.js:20:6200)
    at https://app.powerbi.com/13.0.11499.187/scripts/customVisualsHost.bundle.min.js:20:14488
    at t.executeSafely (https://app.powerbi.com/13.0.11499.187/scripts/customVisualsHost.bundle.min.js:20:18033)
    at t.init (https://app.powerbi.com/13.0.11499.187/scripts/customVisualsHost.bundle.min.js:20:14439)
    at i.init (https://app.powerbi.com/13.0.11499.187/scripts/customVisualsHost.bundle.min.js:21:35819)
    at i.executeMessage (https://app.powerbi.com/13.0.11499.187/scripts/customVisualsHost.bundle.min.js:21:42074)
    at i.onMessageReceived (https://app.powerbi.com/13.0.11499.187/scripts/customVisualsHost.bundle.min.js:21:41772)

如果我在异常上暂停,我最终会在这里:

/******/                var error = new Error();
/******/                onScriptComplete = function (event) {
/******/                    // avoid mem leaks in IE.
/******/                    script.onerror = script.onload = null;
/******/                    clearTimeout(timeout);
/******/                    var chunk = installedChunks[chunkId];
/******/                    if(chunk !== 0) {
/******/                        if(chunk) {
/******/                            var errorType = event && (event.type === 'load' ? 'missing' : event.type);
/******/                            var realSrc = event && event.target && event.target.src;
/******/                            error.message = 'Loading chunk ' + chunkId + ' failed.n(' + errorType + ': ' + realSrc + ')';
/******/                            error.name = 'ChunkLoadError';
/******/                            error.type = errorType;
/******/                            error.request = realSrc;
/******/            ---->           chunk[1](error);
/******/                        }
/******/                        installedChunks[chunkId] = undefined;
/******/                    }
/******/                };

"因承诺被拒绝而暂停" 但可惜的是,我真的不知道我在看什么,或者为了什么。

这是我的webpack.config.js:

const path = require("path");
const fs = require("fs");
const webpack = require("webpack");
console.log(require.resolve("powerbi-visuals-webpack-plugin"));
const PowerBICustomVisualsWebpackPlugin = require("powerbi-visuals-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const Visualizer = require("webpack-visualizer-plugin");
const ExtraWatchWebpackPlugin = require("extra-watch-webpack-plugin");
// api configuration
const powerbiApi = require("powerbi-visuals-api");
// visual configuration json path
const pbivizPath = "./pbiviz.json";
const pbivizFile = require(path.join(__dirname, pbivizPath));
// the visual capabilities content
const capabilitiesPath = "./capabilities.json";
const capabilitiesFile = require(path.join(__dirname, capabilitiesPath));
const pluginLocation = "./.tmp/precompile/visualPlugin.ts"; // path to visual plugin file, the file generates by the plugin
// string resources
// const resourcesFolder = path.join(".", "stringResources");
// const localizationFolders = fs.readdirSync(resourcesFolder);
const localizationFolders = [];
const resourcesFolder = ".";
// babel options to support IE11
const babelOptions = {
    presets: [
        [
            require.resolve("@babel/preset-env"),
            {
                targets: {
                    ie: "11",
                },
                useBuiltIns: "entry",
                modules: false,
                corejs: "3",
            },
        ],
    ],
    sourceType: "unambiguous", // Tell babel that the project can contain different module types, not only es2015 modules.
    cacheDirectory: path.join(".tmp", "babelCache"),
};
module.exports = {
    entry: {
        "visual.js": pluginLocation,
    },
    target: "web",
    node: {
        fs: "empty",
    },
    optimization: {
        concatenateModules: false,
        // minimize: true, // enable minimization for create *.pbiviz file less than 2 Mb, can be disabled for dev mode
    },
    devtool: "source-map",
    mode: "development",
    module: {
        rules: [
            {
                parser: {
                    amd: false,
                },
            },
            {
                test: /(.ts)x|.ts$/,
                include: /powerbi-visuals-|src|precompile\visualPlugin.ts/,
                use: [
                    {
                        loader: require.resolve("babel-loader"),
                        options: babelOptions,
                    },
                    {
                        loader: require.resolve("ts-loader"),
                        options: {
                            transpileOnly: false,
                            experimentalWatchApi: false,
                        },
                    },
                ],
            },
            {
                test: /(.js)x|.js$/,
                use: [
                    {
                        loader: require.resolve("babel-loader"),
                        options: babelOptions,
                    },
                ],
            },
            {
                test: /.json$/,
                loader: require.resolve("json-loader"),
                type: "javascript/auto",
            },
            {
                test: /.less$/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                    },
                    {
                        loader: require.resolve("css-loader"),
                    },
                    {
                        loader: require.resolve("less-loader"),
                        options: {
                            paths: [path.resolve(__dirname, "..", "node_modules")],
                        },
                    },
                ],
            },
            {
                test: /.css$/,
                use: [
                    {
                        loader: MiniCssExtractPlugin.loader,
                    },
                    {
                        loader: require.resolve("css-loader"),
                    },
                ],
            },
            {
                test: /.(woff|ttf|ico|woff2|jpg|jpeg|png|webp)$/i,
                use: [
                    {
                        loader: "base64-inline-loader",
                    },
                ],
            },
        ],
    },
    resolve: {
        extensions: [".wasm", ".tsx", ".ts", ".jsx", ".js", ".css"],
    },
    output: {
        path: path.join(__dirname, "/.tmp", "drop"),
        publicPath: "https://localhost:8080/assets/",
        chunkFilename: "[name].js",
        filename: "[name]",
    },
    devServer: {
        disableHostCheck: true,
        contentBase: path.join(__dirname, ".tmp", "drop"), // path with assets for dev server, they are generated by webpack plugin
        compress: true,
        port: 8080, // dev server port
        publicPath: "https://localhost:8080/assets/",
        hot: false,
        inline: false,
        // cert files for dev server
        https: {
            pfx: fs.readFileSync(path.join(__dirname, "node_modules/powerbi-visuals-tools/certs/PowerBICustomVisualTest_public.pfx")), // for windows
            passphrase: "##########",
        },
        headers: {
            "access-control-allow-origin": "*",
            "cache-control": "public, max-age=0",
        },
    },
    externals: {
        "powerbi-visuals-api": "null",
        fakeDefine: "false",
        corePowerbiObject: "Function('return this.powerbi')()",
        realWindow: "Function('return this')()",
    },
    plugins: [
        new MiniCssExtractPlugin({
            filename: "visual.css",
            chunkFilename: "[id].css",
        }),
        new Visualizer({
            filename: "webpack.statistics.dev.html",
        }),
        // visual plugin regenerates with the visual source, but it does not require relaunching dev server
        new webpack.WatchIgnorePlugin([
            path.join(__dirname, pluginLocation),
            "./.tmp/**/*.*",
        ]),
        // custom visuals plugin instance with options
        new PowerBICustomVisualsWebpackPlugin({
            ...pbivizFile,
            capabilities: capabilitiesFile,
            stringResources: localizationFolders.map((localization) => path.join(
                resourcesFolder,
                localization,
                "resources.resjson",
            )),
            apiVersion: powerbiApi.version,
            capabilitiesSchema: powerbiApi.schemas.capabilities,
            pbivizSchema: powerbiApi.schemas.pbiviz,
            stringResourcesSchema: powerbiApi.schemas.stringResources,
            dependenciesSchema: powerbiApi.schemas.dependencies,
            devMode: true,
            generatePbiviz: false,
            generateResources: true,
            modules: true,
            visualSourceLocation: "../../src/visual",
            pluginLocation,
            packageOutPath: path.join(__dirname, "dist"),
        }),
        new ExtraWatchWebpackPlugin({
            files: [
                pbivizPath,
                capabilitiesPath,
            ],
            dirs: [
                "assets",
            ],
        }),
        new webpack.ProvidePlugin({
            window: "realWindow",
            define: "fakeDefine",
            powerbi: "corePowerbiObject",
        }),
    ],
};

它现在可以工作了。我认为,事实证明,这是错误的组合,由于在Power BI中调试的困难而变得更加成问题。大多数见解来自此示例和此模板。我不确定,现在感恩节假期之后,哪一件事解决了它,但这里有一些关键的变化:

导入 WebAssembly:在visual.ts中,我引用了 emscripten 库,如下所示:

import module from "emscriptenModule";
const moduleWasm = require("../node_modules/.../module.wasm");
import "regenerator-runtime/runtime";
...
module({
    locateFile(filename: string) {
        if (filename.endsWith(".wasm")) {
            return moduleWasm;
        }
        return filename;
    },
}).then((module) => {
    this.module = module;
    this.Init();
});

webpack.config.js 更改:

module: {
    rules: [
        {
            test: /module.js$/,
            loader: "exports-loader",
        },
        // wasm files should not be processed but just be emitted and we want
        // to have their public URL.
        {
            test: /module.wasm$/,
            type: "javascript/auto",
            loader: "file-loader",
        },
    ],
},

我还需要将 babel-polyfill 和 regenerator-runtime 添加到我的 package.json 中。

编辑 当转移到生产环境时,我意识到输出依赖于 localhost:8080 的开发服务器。它现在正在处理以下附加更改:

webpack.config.js

        {
            test: /module.wasm$/,
            type: "javascript/auto",
            loader: "base-64-loader", // <--
        },
...
  output: {
    path: path.join(__dirname, ".tmp", "drop"),
    publicPath: "assets", // <--
    chunkFilename: "[name].js",
    filename: "[name]",
  },

相关内容

  • 没有找到相关文章

最新更新