尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

webpack 5 如何用 VirtualUrlPlugin 注册 virtual: 协议的虚拟模块并支持异步 source

webpack 5 如何用 VirtualUrlPlugin 注册 virtual: 协议的虚拟模块并支持异步 source webpack 5 如何用 VirtualUrlPlugin 注册 virtual: 协议的虚拟模块并支持异步 source【免费下载链接】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当模块内容不是来自磁盘上的真实文件而是需要在构建时由代码动态生成比如注入版本号、拼装路由表、输出 JSON 数据可以直接在 webpack 5 中用VirtualUrlPlugin注册自定义 URL 协议默认virtual:下的虚拟模块并在入口文件里像导入普通模块一样import它们。插件的source支持同步字符串、同步函数和异步函数三种形式因此生成内容需要等待异步 I/O读文件、查数据库等时也能接入构建。仓库自带的完整示例位于 examples/virtual-modules插件实现见 lib/schemes/VirtualUrlPlugin.js。示例测试的运行环境要求 Node.js 14 及以上见 examples/virtual-modules/test.filter.js当前仓库的 webpack 版本为 5.110.3见 package.json。准备工作入口文件按virtual:协议导入在入口文件示例中的 example.js中用virtual:模块id的写法导入未注册到磁盘上的模块import { msg } from virtual:my-module; import myAsyncMessage from virtual:my-async-module; import { version } from virtual:build-info; console.log(msg); // Output from virtual module console.log(myAsyncMessage); // Output async-value console.log(version); // Output value of 1.0.0这里的my-module、my-async-module、build-info都不是文件路径而是后面在插件配置中注册的模块 id。在 webpack.config.js 中注册 VirtualUrlPlugin插件通过webpack.experiments.schemes.VirtualUrlPlugin使用在 lib/index.js 中导出。第一个参数是模块 id 到内容的映射第二个参数是协议名或选项对象不传第二个参数时默认协议为virtual源码中的DEFAULT_SCHEME virtual见 lib/schemes/VirtualUrlPlugin.js。以下配置基于示例 webpack.config.js 精简而来只保留完成本场景必需的部分示例中require(../../)是仓库内部引用方式在自己的项目里应写为require(webpack)use strict; const path require(path); const webpack require(webpack); const VERSION 1.0.0; /** type {import(webpack).Configuration} */ const config { mode: development, // 示例针对 node 目标任意 target 均可 target: node, output: { path: path.resolve(__dirname, dist) }, plugins: [ new webpack.experiments.schemes.VirtualUrlPlugin( { // 形式 1静态字符串 my-module: export const msg from virtual module, // 形式 2异步函数构建时 await 后返回模块源码 my-async-module: async () { const value await Promise.resolve(async-value); return export default ${value}; }, // 形式 3对象形式source 可以是同步/异步函数 build-info: { source() { return export const version ${VERSION}; }, // Re-evaluate this value at each compilation, useful when getting a value from a variable version: true } }, { context: auto } ) ] }; module.exports config;三个构造形式的说明均来自示例与 类型声明 declarations/plugins/schemes/VirtualUrlPlugin.d.ts字符串直接作为模块源码适合内容固定的场景函数可 async在构建时调用返回值作为模块源码异步函数就是标题中「支持异步 source」的入口示例中my-async-module用await Promise.resolve(...)演示了异步取值后拼接源码对象{ source, type, context, version }。source同样是可为 async 的函数且会收到loaderContext参数type可指定模块类型以让 loader 生效version用于缓存失效控制。两个选项的含义context虚拟模块的上下文路径。传auto也是不传时的默认值时插件会尝试从模块 id 本身解析上下文也可以传字符串路径或留空留空时取compiler.context见 lib/schemes/VirtualUrlPlugin.js。协议名第二个参数直接传字符串如my-custom-scheme即可注册非virtual:的协议示例中第二个插件实例用my-custom-scheme:my-module导入了自定义协议下的模块。让异步 source 参与 watch 重建source(loaderContext)形式的函数在 watch 模式下需要手动声明依赖否则内容变化不会触发虚拟模块重建。示例中的两种写法来自 webpack.config.jsroutes: { source( /** type {import(webpack).LoaderContextunknown} */ loaderContext ) { // 监听 routesPath 目录下文件的增删触发虚拟模块重建 loaderContext.addContextDependency(routesPath); const files fs.readdirSync(routesPath); return export const routes {${files .map( (key) ${key.split(.)[0]}: () import(./routes/${key}) ) .join(,\n)}}; } }, code-from-file: { async source( /** type {import(webpack).LoaderContextunknown} */ loaderContext ) { const pathToFile path.resolve(__dirname, ./code.js); // 文件内容变化时触发重建 loaderContext.addDependency(pathToFile); const code await fs.promises.readFile(pathToFile, utf8); return code; } }注意code-from-file的source本身就是 async 函数用fs.promises.readFile异步读文件后返回内容这就是「异步 source」在真实场景读磁盘文件中的用法。可选让 loader 处理虚拟模块示例注释明确说明 Loaders will work with virtual modules给虚拟模块配置type再配合module.rules中对应的 loader 即可。例如用 ts-loader 处理 TypeScript 虚拟模块module: { rules: [ { test: /\.tsx?$/, loader: ts-loader, options: { transpileOnly: true } } ] }, // 插件内 my-typescript-module: { type: .ts, source: () const value: string value-from-typescript; export default value; }, hello.ts: export const hello hello;其中hello.ts直接把 id 命名为hello.ts同样能命中.tsx?规则。执行构建并验证输出在项目根目录执行构建然后运行产物npx webpack node dist/main.js示例入口example.js中每一行console.log都标注了文档示例输出msg输出from virtual modulemyAsyncMessage输出async-valueversion输出1.0.0。上面的精简配置对应前三个导入运行产物应打印这三个值。构建成功时示例的 stats 输出文档示例摘自 examples/virtual-modules/README.md形如asset output.js 19.8 KiB [emitted] (name: main) webpack X.X.X compiled successfully虚拟模块会以virtual:模块id的名称出现在产物注释中例如示例产物dist/output.js里有/*!*************************!*\ !*** virtual:my-module ***! \*************************/ const msg from virtual module异步模块同样被正常内联为const __WEBPACK_DEFAULT_EXPORT__ (async-value);。排查与限制模块 id 未注册导入的virtual:xxx没有出现在插件的 modules 映射中时编译会报ModuleNotFoundError错误信息为Cant resolve virtual module xxx见 lib/schemes/VirtualUrlPlugin.js。核对导入处的 id 与插件配置中的 key 是否一致即可。动态取值与缓存version字段用于声明值的外部依赖。示例中build-info设了version: true注释说明其用途是 Re-evaluate this value at each compilation, useful when getting a value from a variable即值来自变量、每次编译都可能变化时应让它每次重新求值而不是命中缓存。协议与上下文默认协议固定为virtual自定义协议必须再新建一个插件实例不能在同一实例内混用两种协议context默认auto从 id 解析不出路径时如纯 idmy-module会回落到compiler.context虚拟模块内部的相对导入会基于该上下文解析。示例对 target 无特殊要求Just for examples, you can use any target上文的target: node只是为了直接运行产物验证。完整的十种写法字符串、同步/异步函数、JSON 类型、TS loader、路由表动态 import、自定义协议、虚拟资产等可直接对照 examples/virtual-modules/README.md 与 examples/virtual-modules/webpack.config.js需要 loader 与异步读取之外的更多组合时以示例为准。【免费下载链接】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),仅供参考
返回列表