我的浏览器应用程序的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
})] : [])
}
};
答案 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之类的东西几乎是一个黑客插件。
答案 1 :(得分:2)
这确实是一个错误的答案,但我设法在工作线程和主线程之间共享块。
线索是
globalObject
的定义必须与(self || this)
相同:output: {
globalObject: "(self || this)"
}
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'
})
}
}
}
};
// so, typescript recognizes this as module
export let dummy = 2;
// insert "polyfill from hell" from here
import("./RealWorkerMain").then(({ init }) => {
init();
});
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