将web worker打包到整个js代码中完整方案

我们在web端用js计算一些较为复杂耗时的运算时,常常需要用到Web Worker技术.他能够在子线程中运算,最终将结果返回主线程,使得主线程不会卡死.特别是3d场景游戏中,Web Worker使用会不可或缺.
在公司项目里,我有负责的canvas引擎开发中,经常会碰到一些需要用worker多线程的复杂运算场景.然而因为是引擎库,我更希望的是不将worker单独编译成一个js文件引入.
我这边的处理是将worker的代码变成字符串,然后通过Blob动态引入,具体代码如下:

    const script = `...` // worker 代码
    const workerBlob = new Blob([script],{type:'text/javascript'})
    const url = URL.createObjectURL(workerBlob)
    const worker = new Worker(url)
    return worker

到这里我们就可以把worker的代码打包进其他js文件,而不是单独的worker文件了,然而这样做最大的问题是些代码很不方便,并且不好调试(worker的代码往往涉及复杂的数学公式和逻辑运算)
我这边的做法是本地开发时,使用单独的worker js文件开发,只有在打包的时候,先将代码预处理成字符串,然后再通过Blob动态引入,关键代码如下:

export function getWorker(fileName:string){
    if ( IS_DEV ) {
        // 调试
        const FileWorker = require(`worker-loader!../worker/${fileName}.worker.js`)
        const worker = new FileWorker.default()
        return worker as Worker
    } else {
       // 打包 
       const scriptWorker = require(`../worker-lib/${fileName}.worker.js`)
       const script = scriptWorker.default
       // ... 跟上面的Blob方式一致
    }
}

预处理的node程序参考如下:
generate-worker.js

const inputPath = './src/lib/worker'
const outputPath = './src/lib/worker-lib'
var fs = require('fs')
var path = require('path')
var filePath = path.resolve(__dirname,inputPath)
var copyFilePath = path.resolve(__dirname,outputPath)

fileDisplay(filePath,copyFilePath)

function fileDisplay(filePath,copyFilePath){
    fs.mkdirSync(copyFilePath)
    const fileList = fs.readdirSync(filePath)
    for (let i = 0;i< fileList.length;i++) {
        const fileName = fileList[i]
        const fileDir = path.join(filePath,fileName)
        const text = fs.readFileSync(fileDir,'utf-8')
        const scriptText = `const script = \`${text}\`; export default script`
        
        const writeFilePath = path.join(copyFilePath,fileName)
        fs.writeFileSync(writeFilePath,scriptText,{ encoding:'utf-8' })
    }
    console.log('generate worker over')
}

然后再package.json中添加generate命令,用来生成worker字符串:
"generate":"rimraf ./src/lib/worker && node generate-worker.js"

然后在打包的命令前面运行generate,例如:npm run generate && npm run build

现在解决了在worker里调试js代码的问题,但是你要非常注意的使用js代码,因为这块js代码是不会经过babel编译,而是直接输出,很有可能调试的时候好好的,但是打包出来有问题(使用了浏览器还不支持的语法)

我这边给出的解决方案是直接用ts写worker代码,然后通过tsc命令将ts文件编译成js文件后,再调用我们写的generate-worker.js将js代码转成字符串,最后调用打包命令,打包已经转成字符串的代码.

我自己的worker目录管理:
ts写的worker文件位置: src/lib/worker
tsc编译成js文件的目录: src/lib/worker-template-lib
generate生成worker字符串的目录:src/lib/worker-lib

tsc命令: "rimraf ./src/lib/worker-template-lib && tsc --outDir ./src/lib/worker-template-lib src/lib/worker/* "

worker.ts 代码修改: const FileWorker = require(worker-loader!../worker/${fileName}.worker.ts)

generate-worker.js里面inputPath修改: const inputPath = './src/lib/worker-template-lib'

到这一步应该差不多能完美解决调试和打包 worker的需求了

标签: none

添加新评论