webpack 5 如何用 new URL 语法打包 Web Worker 并处理加载路径
webpack 5 如何用 new URL 语法打包 Web Worker 并处理加载路径【免费下载链接】webpackA bundler for javascript and friends. Packs many modules into a few bundled assets. Code Splitting allows for loading parts of the application on demand. Through loaders, modules can be CommonJs, AMD, ES6 modules, CSS, Images, JSON, Coffeescript, LESS, ... and your custom stuff.项目地址: https://gitcode.com/GitHub_Trending/web/webpack如果你的项目里用new Worker(./worker.js)这种字符串路径创建 Workerwebpack 无法把它当作模块打包部署到子路径后 Worker 文件也经常加载失败。webpack 5 的解决方案是让new Worker()的第一个参数写成new URL(./worker.js, import.meta.url)webpack 会在构建时识别这种写法把 Worker 脚本编译成独立的入口 chunk并在运行时用 publicPath 拼接出正确的加载地址。本文以仓库中 examples/worker 示例工程为蓝本给出完整的源码、配置、构建验证和路径解析机制。webpack 识别哪些 Worker 写法Worker 打包由 lib/dependencies/WorkerAndWorkletPlugin.js 中的WorkerAndWorkletPlugin处理。该插件默认识别以下写法见WORKER_DEFAULT_SYNTAX常量WorkerSharedWorkernavigator.serviceWorker.register()Worker from worker_threads其中第一种参数必须能解析出文件 URLnew URL(./worker.js, import.meta.url)就是标准写法。插件对匹配到的表达式做两件事把new URL(...)替换为运行时的 URL 拼接代码并为每个 Worker 创建一个带独立 runtime 的入口源码注释Assigns a unique runtime to the entry so each worker gets its own runtime chunk。也就是说每个 Worker 会拥有自己的一份 webpack runtime不与主 bundle 共用。准备示例项目示例包含 5 个文件全部收录在 examples/worker/README.md。下面按依赖顺序给出与示例一致fibonacci.js—— 主线程和 Worker 共用的计算模块export function fibonacci(n) { return n 1 ? 0 : n 2 ? 1 : fibonacci(n - 1) fibonacci(n - 2); }fib-worker.js—— 通过new Worker加载的 Workeronmessage async event { const { fibonacci } await import(./fibonacci); const value JSON.parse(event.data); postMessage(fib(${value}) ${fibonacci(value)}); };chat-module.js与chat-worker.js——SharedWorker版本用于演示多 Worker 场景// chat-module.js export const history []; export const add (content, from) { if (history.length 10) history.shift(); history.push(${from}: ${content}); };// chat-worker.js import { history, add } from ./chat-module; onconnect function (e) { for (const port of e.ports) { port.onmessage event { const msg event.data; switch (msg.type) { case message: add(msg.content, msg.from); // fallthrough case history: port.postMessage({ type: history, history }); break; } }; } };example.js—— 主入口关键部分是两个 Worker 的创建代码示例完整文件还有 DOM 绑定逻辑此处省略/// CHAT with shared worker /// const chatWorker new SharedWorker( new URL(./chat-worker.js, import.meta.url), { name: chat, type: module } ); /// FIBONACCI with worker /// const fibWorker new Worker(new URL(./fib-worker.js, import.meta.url), { name: fibonacci, type: module /* webpackEntryOptions: { filename: workers/[name].js } */ }); fib2.addEventListener(change, () { try { const value parseInt(fib2.value, 10); fibWorker.postMessage(${value}); } catch (e) { output2.innerText e.message; } }); fibWorker.onmessage event { output2.innerText event.data; };这里有两处值得注意的写法new URL(./xxx.js, import.meta.url)—— 相对当前模块解析 Worker 文件是 webpack 打包的前提。/* webpackEntryOptions: { filename: workers/[name].js } */—— 魔法注释把该 Worker 入口的输出文件名指定为workers/子目录下的[name].js从而把 Worker 产物和普通 chunk 分开。[name]是入口名示例的 stats 输出中两个 Worker 入口名分别为fibonacci和chat。配置 output 部分示例的 examples/worker/webpack.config.js 完整内容如下与 Worker 加载路径直接相关的是output段use strict; const path require(path); /** type {import(webpack).Configuration} */ const config { entry: ./example.js, output: { path: path.join(__dirname, dist), filename: [name].js, chunkFilename: [name].js, publicPath: /dist/ }, optimization: { concatenateModules: true, usedExports: true, providedExports: true, chunkIds: deterministic // To keep filename consistent between different modes (for example building only) } }; module.exports config;publicPath: /dist/决定运行时拼接 Worker URL 时使用的路径前缀chunkIds: deterministic的注释说明其用途是让不同构建模式下 chunk 文件名保持一致。在自己的项目中按上面的结构配置entry和output然后执行一次标准构建例如npx webpack --config webpack.config.js。构建产物与验证构建成功后README 中记录的产物如下文档示例Unoptimized 模式asset main.js 11.6 KiB [emitted] (name: main) asset workers/fibonacci.js 4.74 KiB [emitted] (name: fibonacci) asset chat.js 839 bytes [emitted] (name: chat) asset 129.js 729 bytes [emitted]可以逐项核对main.js主入口fib-worker.js和chat-worker.js没有被打进主 bundle而是各自成为独立入口workers/fibonacci.js对应入口fibonaccichat.js对应入口chat129.jsfibonacci.js被主线程的import()和 Worker 内的import()共同引用被提取为共享异步 chunkstats 中标注了两个来源./example.js 70:30-51和./fib-worker.js 2:29-50。再打开dist/main.js检查 URL 转换结果。源文件里的new Worker(new URL(./fib-worker.js, import.meta.url), { ... })被替换为摘自 README 记录的 dist/main.js__webpack_require__.p /dist/; __webpack_require__.b (typeof document ! undefined document.baseURI) || self.location.href; __webpack_require__.u (chunkId) { // return url for filenames not based on template if (chunkId 721) return workers/fibonacci.js; // return url for filenames based on template return (chunkId 377 ? chat : chunkId) .js; }; const fibWorker new Worker(new URL(/* worker import */ __webpack_require__.p __webpack_require__.u(721), __webpack_require__.b), { name: fibonacci, type: undefined /* webpackEntryOptions: { filename: workers/[name].js } */ });运行时验证部署一个引用main.js的页面后在示例页面“Computing fibonacci with worker”输入框填入数字Worker 计算完成后通过postMessage回传页面output2区域显示fib(n) ...格式的结果格式来自fib-worker.js的模板字符串。如果页面部署在子目录下且 Worker 加载失败问题通常出在publicPath与实际部署路径不一致见下一节。加载路径是如何解析的上面构建产物揭示了路径处理机制Worker 的最终 URL 由三部分在运行时拼出__webpack_require__.p—— 取自output.publicPath示例中为/dist/__webpack_require__.u(chunkId)—— 返回 chunk 文件名带webpackEntryOptions.filename的 Worker 走workers/fibonacci.js这条分支new URL(url, base)的第二个参数__webpack_require__.b—— 取document.baseURI无 document 时退回self.location.href保证相对/绝对路径按页面实际位置解析。也就是说 Worker 脚本不需要写死绝对地址publicPath配对了部署路径浏览器侧的解析就能命中文件。两个相关的补充点Worker 内部加载额外 chunk示例中fib-worker.js里的await import(./fibonacci)在 Worker 内编译为__webpack_require__.e(129)Worker 的 runtime 用importScripts(__webpack_require__.p __webpack_require__.u(chunkId))拉取129.js见 README 记录的dist/workers/fibonacci.js中 importScripts chunk loading 段。注意这里同样是publicPath 文件名所以 Worker 内部动态导入同样依赖publicPath配置正确。Worker 放在独立路径时如果 Worker 脚本不和其他脚本放在同一目录可以在output下设置workerPublicPath。declarations/WebpackOptions.d.ts 中的说明原文If not set, webpack will use the publicPath. Dont set this option unless your worker scripts are located at a different path from your other script files. 即默认复用publicPath只有 Worker 实际部署位置不同时才需要单独设置。另外 README 产物中还有一个细节源码里new Worker/new SharedWorker第二参数写的type: module在构建产物中变为type: undefinedWorker 运行时按 classic 脚本方式通过importScripts加载 chunk。这是示例构建的实际输出若你的 Worker 依赖 ES module 特性需要以实际构建产物为准核对。边界与常见报错魔法注释类型错误会产出 warning来自 WorkerAndWorkletPlugin.js 源码webpackEntryOptions必须是对象webpackChunkName必须是字符串对应入口namewebpackIgnore必须是 boolean。构建后检查 warning 列表即可定位。每个 Worker 独立 runtime由ensureRuntime逻辑保证Worker chunk 与主 bundle 之间不会共享 runtime 代码产物里出现多份webpackBootstrap属正常现象而不是重复打包。不打包某个 Worker在对应表达式上写/* webpackIgnore: true */插件解析后直接跳过该 Worker。插件还支持navigator.serviceWorker.register()与worker_threads的Worker处理机制相同本文示例仅覆盖Worker与SharedWorker。下一步可以对照 examples/module-worker 查看 ESM Worker 的另一种组织方式或在output中补充chunkFilename模板与workerPublicPath来适配自己的目录结构。【免费下载链接】webpackA bundler for javascript and friends. Packs many modules into a few bundled assets. Code Splitting allows for loading parts of the application on demand. Through loaders, modules can be CommonJs, AMD, ES6 modules, CSS, Images, JSON, Coffeescript, LESS, ... and your custom stuff.项目地址: https://gitcode.com/GitHub_Trending/web/webpack创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考