Javascript 如何使用worker loader使用webworkers创建typescript库

Javascript 如何使用worker loader使用webworkers创建typescript库,javascript,typescript,webpack,web-worker,worker-loader,Javascript,Typescript,Webpack,Web Worker,Worker Loader,我尝试用web工作者创建typescript库。当我用webpack dev server测试我的代码时,一切看起来都很好,找到了所有文件,但当我让npm运行build并尝试在另一个本地项目(npm install/local/path)中使用lib时,我看到了GEThttp://localhost:8080/X.worker.js 在浏览器控制台中 webpack.config.js: const path = require('path'); module.exports = {

我尝试用web工作者创建typescript库。当我用webpack dev server测试我的代码时,一切看起来都很好,找到了所有文件,但当我让npm运行build并尝试在另一个本地项目(npm install/local/path)中使用lib时,我看到了
GEThttp://localhost:8080/X.worker.js 
在浏览器控制台中

webpack.config.js:

const path = require('path');

module.exports = {
    devtool: 'inline-source-map',
    entry: {
        'mylib': './src/index.ts',
        'mylib.min': './src/index.ts',
    },
    output: {
        path: path.resolve(__dirname, '_bundles'),
        filename: '[name].js',
        libraryTarget: 'umd',
        library: 'mylib',
        umdNamedDefine: true
    },
    resolve: {
        extensions: ['.ts', '.tsx', '.js']
    },
    optimization: {
        minimize: true
    },
    module: {
        rules: [
            {
                test: /\.tsx?$/,
                loader: 'awesome-typescript-loader',
                exclude: /node_modules/,
                query: {
                    declaration: false,
                }
            },
            {
                test: /\.worker\.js$/,
                use: {
                    loader: "worker-loader"
                }
            },
        ]
    }
};
tsconfig.json

{
    "compilerOptions": {
        "target": "es5",
        "module": "es6",
        "lib": [
            "webworker",
            "es2015",
            "dom"
        ],
        "moduleResolution": "node",
        "sourceMap": true,
        "strict": true,
        "alwaysStrict": true,
        "outDir": "lib",
        "resolveJsonModule": true,
        "declaration": true,
        "skipLibCheck": true,
        "allowJs": true
    },
    "include": [
        "**/*.ts",
        "**/*.tsx"
    ],
    "exclude": [
        "node_modules",
        "lib",
    ]
}
package.json

{
  "name": "mylib",
  "version": "1.0.0",
  "description": "",
  "main": "_bundles/mylib.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "webpack": "webpack",
    "build": "rm -rf ./lib && tsc",
    "serve": "webpack-dev-server",
    "clean": "rm -rf _bundles lib lib-esm",
    "newbuild": "npm run clean && tsc && tsc -m es6 --outDir lib-esm && webpack"
  },
  "repository": {
    "type": "git",
    "url": "..."
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "bugs": {
    "url": "..."
  },
  "homepage": "...e",
  "devDependencies": {
    "prettier": "^2.1.2",
    "tslint": "^6.1.3",
    "tslint-config-prettier": "^1.18.0",
    "worker-loader": "^3.0.5"
  },
  "dependencies": {
     ...
  }
}
有关如何导入工人的示例:

import X from "worker-loader!./X";

几个月前,我发现自己处于完全相同的境地。我找到了一个适合我的解决方案,但首先让我们讨论一下为什么会发生这种情况

问题是: 这里有3层

  • 库的开发层
  • 库的构建层
  • 使用库的构建的应用程序
  • 第1层很简单。在任何您想要创建新工作程序的文件中,比如它的
    index.ts
    ,您都可以从“worker loader.”/X“导入X。您的
    index.ts
    确切地知道在哪里可以找到工作文件。这就是为什么在你的网页开发服务器上工作的原因

    第二层是事情变得奇怪的地方。当您使用
    辅助加载程序
    处理辅助文件时,webpack会输出多个文件。您的配置显示
    文件名:'[name].js'
    ,它将为源文件夹中的每个文件输出一个文件,所有文件都位于
    \u bundles
    文件夹中的同一级别。Webpack可以从“worker loader.”/X“
    中查看您的
    导入X,还可以查看导入文件的目标名称和位置,以及执行导入操作的文件。它将web worker文件在
    index.js
    输出捆绑包中的位置重写为相对于捆绑包其余部分的绝对路径。通过使用worker loader中的选项,可以更仔细地控制这一点。但这并不能真正解决问题,因为您只是将
    publicPath
    设置为绝对路径,这将导致我们进入步骤3

    第三层,你试图消费你的软件包,是出问题的地方。你永远无法预料到人们可能会在代码中
    从“你的库”
    导入{makeAWorker}。无论从何处导入,构建文件(在消费者应用程序的node_模块中)都将使用webpack写入
    index.js
    构建中的路径来查找工作文件,但现在绝对路径是相对于消费者项目的(通常是主路径,如index.html所在的位置),不是到生成的node_modules文件夹。因此,您的消费者应用程序不知道在哪里可以找到worker文件

    我的解决方案:一点小技巧 在我的场景中,我决定我的worker文件的内容足够简单,可以从字符串创建一个worker,并以这种方式导入它。例如,工作文件如下所示:

    //worker.ts
    //从匿名函数体生成辅助函数
    导出默认URL.createObjectURL(
    新斑点([
    '(',
    函数(){
    //此处的实际工作人员内容
    }.toString(),
    ')()', ],
    {type:'application/javascript'}
    )
    );
    
    然后在我想要产生工作者的地方:

    //index.ts
    从“/worker”导入workerScript;
    const myWorker=new Worker(workerScript,{Worker\u options});
    
    这是因为现在您不再要求webpack创建文件并为您写入正确的导入位置。最终,您的工作脚本包中甚至没有单独的文件。您可以完全放弃
    工作加载程序。您的
    index.ts
    将创建Blob及其URL,您的使用者应用程序将在运行时动态生成的URL处找到工作脚本

    真是个黑客 这种方法有一些严重的缺点,这让我提出了这个问题。问题是,在worker脚本中,您实际上没有导入任何内容的选项。我很幸运,因为我的工人相对简单。它依赖于单个节点_模块,该模块本身没有依赖关系。我一开始只是将该模块的源代码包含在脚本中,但由于我有多个脚本需要相同的外部模块,因此在生成该模块时,我最终将其作为一段数据传递给工作程序:

    从'/utils'导入{Rainbow};
    风险值数据={
    身份证件
    资料
    彩虹字符串:彩虹.toString(),
    };
    myWorker.postMessage(数据);
    
    然后在worker中,我只需将
    彩虹串
    转换回一个函数并使用它。如果您想了解更多详细信息,可以查看我使用以下方法构建的库:。查看
    src/TopoLayer.ts
    文件以了解如何使用worker,查看
    src//workers
    文件夹以了解如何设置worker blob

    结论 我想一定有更好的办法。一个可能的快速修复方法是编写一个复制脚本,将工作程序文件从
    node\u modules/yourLibrary
    复制到消费者应用程序的build文件夹中。但这并不能带来很好的可移植性,其他人也必须做同样的事情才能让你的库使用他们的应用程序。我的解决方案并不完美,但它适用于简单的ish工作脚本。我仍在考虑一个更强大的解决方案,允许工人自己进口

    编辑:正确的解决方案: 所以在写了这个答案之后,我很受鼓舞地加入了网页加载程序repo中的对话。显然,当webpack 5在大约2个月前发布时,他们增加了对这种语法的支持:

    newworker(新URL('./path/to/Worker.js',import.meta.URL))
    
    我还没有尝试过这个,但它看起来像是你需要的解决方案(也是我在2个月前提出我的破解方案时需要的解决方案)。试试这个-这可能正是你需要告诉webpack捆绑你的库,同时仍然保持工作脚本和导入它的脚本之间的关系