Webpack:Webworker和Web代码之间共享代码的常见块?

时间:2018-03-19 00:20:32

标签: javascript webpack bundle web-worker code-splitting

我的浏览器应用程序的Web和Web worker部分之间共享了大量代码。

如何告诉webpack将我的代码拆分为常见的块,以保证结果100%工作?

在我告诉webpack生成公共块(它确实如此)之后,webworker代码中断(在运行时失败)。即使我修复了未定义的琐碎窗口"错误,工人什么都不做。

我认为这与webpack" target"选项,默认情况下设置为" web"。但我需要" web"目标因为我没有纯粹的网络工程师代码。

我也不能做多个webpack配置,因为我不能用多个配置做常见的块事...

我该怎么办?

如果有人感兴趣:我正在尝试为我的应用构建一个最小尺寸的版本,其中包括monaco编辑器(提供工作人员):

https://github.com/Microsoft/monaco-editor/blob/master/docs/integrate-esm.md

您可以在此处(页面底部)看到入口点由1个主要入口文件+工作人员组成。

目前至少有6 MB被浪费,因为我使用的代码重复,并且由于此问题目前无法拆分。这是很多浪费的流量。

有什么想法吗? :)

我的webpack 4.1.1配置基本上是:

module.exports = (env, options) => {
    const mode = options.mode;
    const isProduction = mode === 'production';
    const outDir = isProduction ? 'build/release' : 'build/debug';

    return {

        entry: {
            "app": "./src/main.tsx",
            "editor.worker": 'monaco-editor/esm/vs/editor/editor.worker.js',
            "ts.worker": 'monaco-editor/esm/vs/language/typescript/ts.worker.js'
        },
        output: {
            filename: "[name].bundle.js",
            path: `${__dirname}/${outDir}`,
            libraryTarget: 'umd',
            globalObject: 'this',
            library: 'app',
            umdNamedDefine: true
        },
        node: {
            fs: 'empty' 
        },
        devtool: isProduction ? undefined : "source-map",
        resolve: {
            extensions: [".ts", ".tsx", ".js", ".json"],
            alias: {
                "@components": path.resolve(__dirname, "src/components"),
                "@lib": path.resolve(__dirname, "src/lib"),
                "@common": path.resolve(__dirname, "src/common"),
                "@redux": path.resolve(__dirname, "src/redux"),
                "@services": path.resolve(__dirname, "src/services"),
                "@translations": path.resolve(__dirname, "src/translations"),
                "@serverApi": path.resolve(__dirname, "src/server-api")
            }
        },
        optimization: isProduction ? undefined : {
            splitChunks: {
                minSize: 30000,
                minChunks: 1,
                name: true,
                maxAsyncRequests: 100,
                maxInitialRequests: 100,
                cacheGroups: {
                    default: {
                        chunks: "all",
                        priority: -100,
                        test: (module) => {
                            const req = module.userRequest;
                            if (!req) return false;
                            return (!/node_modules[\\/]/.test(req));
                        },
                    },
                    vendor: {
                        chunks: "all",
                        test: (module) => {
                            const req = module.userRequest;
                            if (!req) return false;
                            if (!/[\\/]node_modules[\\/]/.test(req)) return false;
                            return true;
                        },
                        priority: 100,
                    }
                }
            },
        },
        module: {
            rules: [...(isProduction ? [] : [
                {
                    enforce: "pre", test: /\.js$/, loader: "source-map-loader",
                    exclude: [
                        /node_modules[\\/]monaco-editor/ 
                    ]
                }
            ]),
            {
                test: require.resolve('jquery.hotkeys'),
                use: 'imports-loader?jQuery=jquery'
            },
            {
                test: /\.tsx?$/,
                loader: "awesome-typescript-loader",
                options: {
                    configFileName: 'src/tsconfig.json',
                    getCustomTransformers: () => {
                        return {
                            before: [p => keysTransformer(p)]
                        };
                    }
                }
            },
            {
                test: /\.(css|sass|scss)$/,
                use: extractSass.extract({
                    use: [
                        {
                            loader: 'css-loader',
                            options: {
                                minimize: isProduction
                            }
                        },
                        {
                            loader: "postcss-loader",
                            options: {
                                plugins: () => [autoprefixer({
                                    browsers: [
                                        'last 3 version',
                                        'ie >= 10'
                                    ]
                                })]
                            }
                        },
                        { loader: "sass-loader" }
                    ],
                    fallback: "style-loader"
                })
            },
            {
                test: /node_modules[\/\\]font-awesome/,
                loader: 'file-loader',
                options: {
                    emitFile: false
                }
            },
            {
                test: { not: [{ test: /node_modules[\/\\]font-awesome/ }] },
                rules: [
                    {
                        test: { or: [/icomoon\.svg$/, /fonts[\/\\]seti\.svg$/] },
                        rules: [
                            { loader: 'file-loader?mimetype=image/svg+xml' },
                        ]
                    }, {
                        test: { not: [/icomoon\.svg$/, /fonts[\/\\]seti\.svg$/] },
                        rules: [
                            {
                                test: /\.svg(\?v=\d+\.\d+\.\d+)?$/,
                                use: {
                                    loader: 'svg-url-loader',
                                    options: {}
                                }
                            },
                        ]
                    },
                    {
                        test: /\.(png|jpg|gif)$/,
                        loader: 'url-loader'
                    },
                    { test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: "url-loader?mimetype=application/font-woff" },
                    { test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: "url-loader?mimetype=application/font-woff" },
                    { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: "url-loader?mimetype=application/octet-stream" },
                    { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: "url-loader" },
                ]
            },

            ]
        },
        plugins: [
            new HardSourceWebpackPlugin({
                cacheDirectory: '../node_modules/.cache/hard-source/[confighash]', configHash: function (webpackConfig) {
                    return require('node-object-hash')({ sort: false }).hash(Object.assign({}, webpackConfig, { devServer: false }));
                },
                environmentHash: {
                    root: process.cwd(),
                    directories: [],
                    files: ['../package-lock.json'],
                }
            }),
            new webpack.ProvidePlugin({
                "window.$": "jquery"
            }),
            new CleanWebpackPlugin(outDir),
            extractSass,
            new HtmlWebpackPlugin({
                title: 'my title',
                filename: 'index.html',
                minify: isProduction ? {
                    collapseWhitespace: true,
                    collapseInlineTagWhitespace: true,
                    removeComments: true,
                    removeRedundantAttributes: true
                } : false,
                template: 'index_template.html',
                excludeChunks: ['ts.worker', "editor.worker"]
            }),
            new webpack.IgnorePlugin(/^((fs)|(path)|(os)|(crypto)|(source-map-support))$/, /vs[\\\/]language[\\\/]typescript[\\\/]lib/)
        ].concat(isProduction ? [new webpack.optimize.LimitChunkCountPlugin({
            maxChunks: 1
        })] : [])
    }
};

4 个答案:

答案 0 :(得分:3)

编辑:好吧,我根据大家的知识编写了一个webpack插件。

https://www.npmjs.com/package/worker-injector-generator-plugin

您可以忽略下面的内容,而使用插件,或者如果您想了解插件的外观并亲自操作(这样就不必依赖我的代码),可以继续阅读。

=========================================== ======

经过大量的研究,我想出了这个解决方案,您需要创建一个注入文件,对于一个简单的例子,您需要https://github.com/webpack-contrib/copy-webpack-plugin,因为它运行得很好...因此,您的设置是:< / p>

entry: {
    "worker": ["./src/worker.ts"],
    "app": ["./src/index.tsx"],
  },

您已经设置了常见的插件,让我们说这个例子。

optimization: {
    splitChunks: {
      cacheGroups: {
        commons: {
          name: 'commons',
          chunks: 'initial',
          minChunks: 2
        },
      }
    }
  },

您现在需要创建一个看起来像这样的注入“ Vanilla JS”:

var base = location.protocol + "//" + location.host;
window = self;

self.importScripts(base + "/resources/commons.js", base + "/resources/worker.js");

然后您可以将其添加到您的工作人员旁边,例如在src/worker-injector.js

中说

并使用复制插件

new CopyPlugin([
      {
        from: "./src/worker-injector.js",
        to: path.resolve(__dirname, 'dist/[name].js'),
      },
    ]),

确保您的输出设置为umd。

output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist'),
    libraryTarget: "umd",
    globalObject: "this",
  }

这不过是一种黑客手段,而是使您可以按原样使用一切,而不必做夸大其词的事情。

如果需要散列(这样复制插件不起作用)功能,则必须生成此文件(而不是复制文件),请参阅此文件。

How to inject Webpack build hash to application code

为此,您必须创建自己的插件,该插件将生成Vanilla js文件并考虑其内部的哈希,您将传递要加载在一起的url,并将哈希附加到它们,这是更加棘手,但是如果您需要哈希,可以使用自定义插件轻松实现。

到目前为止,似乎还没有其他方法。

我可能自己编写了可以解决该问题并创建注入器的插件,但是我确实认为这更多的是hack,不应该被接受为解决方案。

稍后我可能会去写出喷油器插件,可能是这样的:

类似new WorkerInjectorGeneratorPlugin({name: "worker.[hash].injector.js", importScripts: ["urlToLoad.[hash].js", secondURLToLoad.[hash].js"])

请参阅此问题以供参考,以及为什么应在webpack中修复该问题,并作为WorkerInjectorGeneratorPlugin之类的东西几乎是一个黑客插件。

https://github.com/webpack/webpack/issues/6472

答案 1 :(得分:2)

这确实是一个错误的答案,但我设法在工作线程和主线程之间共享块。

线索是

  1. globalObject的定义必须与(self || this)相同:
output: {
    globalObject: "(self || this)"
}
  1. Webpack加载具有document.createElement('script')document.head.appendChild()序列的块,这在工作程序上下文中不可用,但是我们有self.importScript。因此,这只是“多文件化”问题。 这是正在工作的“ polyfill”(直接来自地狱):
console.log("#faking document.createElement()");
(self as any).document = {
    createElement(elementName: string): any {
        console.log("#fake document.createElement", elementName);
        return {};
    },
    head: {
        appendChild(element: any) {
            console.log("#fake document.head.appendChild", element);
            try {
                console.log("loading", element.src);
                importScripts(element.src);
                element.onload({
                    target: element,
                    type: 'load'
                })
            } catch(error) {
                element.onerror({
                    target: element,
                    type: 'error'
                })
            }
        }
    }
};
  1. 通过使用动态导入确保在安装Polyfill后 可以解析您的真实代码。假定正常的“ worker main”位于“ ./RealWorkerMain”中,即为“ main worker script”:
// so, typescript recognizes this as module
export let dummy = 2;

// insert "polyfill from hell" from here

import("./RealWorkerMain").then(({ init }) => {
    init();
});
  1. 您可能需要在webpack中配置动态import,因为here也不容易,this answer很有帮助。

答案 2 :(得分:1)

您正在寻找通用library target,又名 umd

  

这会在所有模块定义下公开您的库,允许   它可以与CommonJS,AMD和全局变量一起使用。

要使您的Webpack包编译为 umd ,您应该配置output属性,如下所示:

output: {
    filename: '[name].bundle.js',
    libraryTarget: 'umd',
    library: 'yourName',
    umdNamedDefine: true,
},

Webpack 4有issue,但如果您仍想使用它,可以通过在配置中添加globalObject: 'this'来解决此问题:

output: {
    filename: '[name].bundle.js',
    libraryTarget: 'umd',
    library: 'yourName',
    umdNamedDefine: true,
    globalObject: 'this'
},

答案 3 :(得分:0)

webpack 5 中引入了对 Native Worker 的支持。有了这个特性,你可以使用简单的 splitChunk 选项在应用代码和 webwoker 之间共享块

{
    optimization: {
        splitChunks: {
            chunks: 'all',
            minChunks: 2,
        },
    },
}
<块引用>

当将资产的新 URL 与 new Worker/new SharedWorker/navigator.serviceWorker.register 结合使用时,webpack 将自动为 Web Worker 创建一个新入口点。

new Worker(new URL("./worker.js", import.meta.url))

选择的语法也允许在没有打包器的情况下运行代码。此语法也可用于浏览器中的原生 ECMAScript 模块。

https://webpack.js.org/blog/2020-10-10-webpack-5-release/#native-worker-support