ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Webpack Loader 实战剖析:基于 examples/loader 示例掌握自定义 Loader、内联请求与 css-loader 规则配置

Webpack Loader 实战剖析:基于 examples/loader 示例掌握自定义 Loader、内联请求与 css-loader 规则配置 Webpack Loader 实战剖析基于 examples/loader 示例掌握自定义 Loader、内联请求与 css-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本文以仓库 examples/loader 这一官方示例为骨架系统讲解 Webpack Loader 的三种典型用法通过内联请求使用自定义 Loader、按文件扩展名自动匹配规则加载 CSS、以及用!前缀强制指定 Loader 链。读完本文你将能独立编写一个最简同步 Loader、读懂 Loader 请求字符串的语法与执行顺序并结合源码理解 Webpack 内部的 loader 解析与运行机制。一、示例全景一个演示文件放在仓库的什么位置examples/loader是 examples 示例合集中的一个入门级演示官方目录导览 examples/README.md 将其归类为 Loaderdemonstrating the usage of loaders演示 Loader 的用法。该目录的全部源文件如下example.js入口文件展示三种 loader 调用方式file.js一个极简的 CommonJS 资源文件导出fooloader.js本文手写的自定义同步 Loadertest.css被 css-loader 处理的 CSS 资源webpack.config.js示例的打包配置template.md / README.md示例文档与构建模板。其中template.md采用示例文档的模板占位机制构建工具 examples/template-common.js 中的replaceResults/replaceBase会将_{{...}}_形式的占位符替换为真实源码、打包产物与统计输出其最终内容即 README.md。也就是说文档中展示的dist/output.js是构建生成的产物快照源码与构建脚本共同构成了一个可复现的实验环境。二、入口文件 example.js三种 Loader 用法一网打尽示例入口 example.js 只有三行require却覆盖了 Loader 使用的三个层面// use our loader console.dir(require(./loader!./file)); // use built-in css loader console.dir(require(./test.css)); // default by extension console.dir(require(!css-loader!./test.css)); // manual第一行require(./loader!./file)是内联请求inline request语法请求字符串中以!分隔 Loader 与资源!左侧是 Loader这里相对路径./loader指向同目录的loader.js!右侧是待处理的资源./file即file.js。Webpack 会先让loader.js处理file.js的源码再把处理结果编译成模块。第二行require(./test.css)是按扩展名自动匹配Webpack 本身不理解 CSS是 webpack.config.js 中的module.rules把/\.css$/匹配到的文件交给css-loader。因此这里无需写出 Loader 名请求字符串里没有!整个字符串就是一个资源路径。第三行require(!css-loader!./test.css)是手动内联指定。注意这里的!css-loader!前面还有一个前导!它表示跳过 webpack.config.js 中配置的规则auto loaders只使用内联写出的 Loader 链。文档注释// manual与第二行的// default by extension形成对照——两者最终命中同一个 css-loader 处理逻辑因此在构建产物中指向同一个模块。浏览器控制台以及历史上的enhanced-requireNode 环境中最终打印的三行输出为{ answer: 42, foo: bar } { foobar: 1234 } { foobar: 1234 }第一行来自./loader!./file自定义 Loader 注入的answer: 42叠加file.js自带的foo: bar后两行都来自同一个 css-loader 产物对象说明按扩展名自动匹配与手动!内联殊途同归并且同一模块只被打包一次。三、写一个最简同步 Loaderloader.js 逐行拆解自定义 Loader 的全部源码只有三行见 loader.jsmodule.exports function(content) { return exports.answer 42;\n content; }这是一个典型的同步 Loader其契约非常简单以 CommonJS 方式导出函数Webpack 在 lib/loaders/loadLoader.js 中通过loadLoader完成对 CommonJS/ESM Loader 模块的加载导出函数即转换器本体第一个参数content是源文件内容这里即file.js的文本exports.foo bar;返回值是转换后的源码字符串Loader 把file.js的内容前面拼接一行exports.answer 42;于是下游模块同时拥有answer与foo两个导出这正是最终模块注释export answer [provided]与export foo [provided]的由来。这种返回字符串的写法是最简单的同步 Loader。若需要返回多个结果或 Source Map官方接口会使用this.callback(null, code, map)若需要异步处理如读取文件、调用外部工具则应调用this.async()获取回调后延后完成。这些上下文能力由 Loader 运行器注入其类型声明可参考 declarations/LoaderContext.d.ts运行实现位于 lib/loaders/LoaderRunner.js。示例为突出Loader 就是纯函数式转换这一核心思想刻意省略了这些进阶 API。需要特别强调Loader 的输入输出都是代码文本因此理论上你能用它把任何内容翻译成 JavaScript——正如项目简介所说通过 loaders 模块可以是 CommonJS、AMD、ES6、CSS、Images、JSON 等等。四、配置文件用 module.rules 把 CSS 交给 css-loader示例配置 webpack.config.js 是理解规则rules驱动的一把钥匙use strict; /** type {import(webpack).Configuration} */ const config { // mode: development || production, module: { rules: [ { test: /\.css$/, loader: css-loader } ] } }; module.exports config;要点解读test: /\.css$/是匹配条件命中该正则的资源路径这里即./test.css会进入这条规则loader: css-loader声明该资源交由css-loader处理项目根目录的yarn.lock中包含了 css-loader 等依赖是搭建示例环境的基础。对于需要多个 Loader 串联的场景配置中可改用use: [style-loader, css-loader]数组文件顶部注释掉的mode: development || production提示该示例在两种模式下均可运行且产物差异巨大见下文Info统计对比这个配置没有给.js文件配置任何规则——file.js走的是内联的./loaderexample.js由入口编译均不需要规则参与。这恰好说明规则的用途是把某类资源映射到默认处理管线而内联请求提供了绕过或叠加规则的即时手段。由于规则的存在入口第二行require(./test.css)才能默认按扩展名工作而第三行的前导!表示仅用内联指定的 css-loader忽略规则Webpack 称之为 no-auto-loaders 语义。两者产物一致验证了规则展开后本质上等价于一条内联 loader 链。五、读懂打包产物 dist/output.js在development模式下构建后文档记录了产物 template.md 中# dist/output.js一节 的核心结构实际文件在运行node build.js后生成于示例目录的dist/下。产物是标准的 webpack 自执行 bundle包含三大部分1. 模块表__webpack_modules__模块 1 ——./loader.js!./file.js注释块明确标出请求来源模块体内依次出现exports.answer 42;与exports.foo bar;直观证明了自定义 Loader 在编译期完成源码改写模块 2 ——../../node_modules/css-loader/dist/cjs.js!./test.cssCSS 被 css-loader 编译为 JS 模块导出经__webpack_require__.d声明的default一个样式列表对象同时注入对css-loader/dist/runtime/noSourceMaps.js模块 3与css-loader/dist/runtime/api.js模块 4两个运行时模块的依赖。可以看到 CSS 文本被放入___CSS_LOADER_EXPORT___.push([module.id, \.some-class {...}, ])模块 3、4 是 css-loader 自带的运行时 helper用于把样式列表拼接成可注入的 CSS 字符串支持media、supports、layer等语法包装。2. webpack 运行时runtime包括模块缓存__webpack_module_cache__、__webpack_require__加载函数以及若干兼容 helper.n获取非 ESM 默认导出、.d定义导出 getter、.ohasOwnProperty 简写、.r标记 ESM 命名空间。这解释了为何打包产物能在浏览器与 Node 中直接执行。3. 入口执行段入口代码里对./loader!./file、./test.css、!css-loader!./test.css的三个require分别被改写成__webpack_require__(1)、__webpack_require__(2)、__webpack_require__(2)——后两个请求共享模块 2再次印证同一资源只编译一次。4. Info两种模式的构建统计模板文档中的# Info一节还给出了同一示例在两种模式下的真实构建统计对比Unoptimizeddevelopmentoutput.js8.72 KiB含 883 bytes 运行时模块注释含[used exports unknown]等未优化标记Production modeoutput.js仅 1.81 KiBminimized运行时缩减到 1.23 KiB模块变为[no exports used]。这组数据是体验 webpack 压缩与 tree-shaking 收效的直观参照实际版本号在生成时被规范化为webpack X.X.X compiled successfully。六、进入源码内联请求解析与 loader 运行器1. 内联请求字符串如何在 NormalModuleFactory 中解析示例中./loader!./file、!css-loader!./test.css这类带!的请求其解析逻辑位于 lib/NormalModuleFactory.js 的resolveRequestArray附近约 L819-L871。核心逻辑为识别请求前缀来决定是否跳过配置规则!开头no-auto-loaders跳过普通normalloader-!开头no-pre-auto-loaders额外跳过前置preloader!!开头则只保留内联 loader完全跳过 pre/normal/post 规则随后用/!/把剩余请求按!分割得到loader 列表 资源路径。因此示例入口中的!css-loader!./test.css与配置文件module.rules中的 loader 是两条会在此处汇合的管线内联 loader 直接并入规则 loader 由匹配的规则追加除非被!/-!/!!禁用。2. Loader 链如何被执行LoaderRunner 的 pitch 与执行顺序打包阶段资源会交给 lib/loaders/LoaderRunner.js 运行 loader 链。该文件实现了经典的pitching phase normal phase两段式执行模型Pitch 阶段从左到右逐个调用每个 loader 导出的pitch函数若存在。LoaderRunner.js L400-L443 的iteratePitchingLoaders从链首开始一旦某个 pitch 返回了非undefined的结果就短路整条链——后续 loader 不再执行这是 style-loader、url-loader 等提前接管类 loader 的实现基础Normal 阶段从右到左pitch 全部为空时才读取资源并逆序执行各 loader 的转换函数。对于a!b!c!file实际数据流是file → c → b → a最左侧 loader 的返回值即模块源码。以本示例为例./loader!./file的 loader 链只有一个成员./loader所以只经历一次转换而 css-loader 模块之所以能工作正是因为 css-loader 内部同时依赖了链上追加的运行时模块。3. Loader 的 this 上下文与更多能力Loader 函数内部的this是运行器注入的 LoaderContext见 declarations/LoaderContext.d.ts提供this.callback、this.async、this.getOptions()、依赖声明等能力。示例loader.js只用了最基础的入参 content、返回字符串子集把概念复杂度降到最低——这是新手入门 Loader 的最佳切入点。七、如何在本仓库运行该示例该示例本身不额外要求构建但其文档与dist/output.js产物是通过仓库的示例构建体系生成的。官方 examples/README.md#building-an-example 记载的流程如下在仓库根目录执行yarn安装依赖在仓库根目录执行yarn setup完成示例环境初始化在仓库根目录执行yarn add --dev webpack-cli安装 CLI进入示例目录执行node build.js例如cd examples/loader node build.js即可生成dist/output.js并刷新文档中的构建统计。如需一次性构建全部示例可在仓库根目录执行npm run build:examples其批处理脚本见 examples/buildAll.js而示例目录清单由 examples/examples.js 递归扫描包含template.md的目录自动生成深度 2。注意仓库本身是只读的上述流程用于本地查看与实验。八、小结与延伸阅读通过 examples/loader 这一最小示例我们可以提炼出三条可复用的经验写 Loader 就是写源码转换函数同步场景只需module.exports function(content){ ... return newContent }异步与 Source Map 场景再引入this.callback/this.async请求字符串是 loader 链的速写loader!loader!resource从左到右书写、从右到左执行前导!/-!/!!控制是否跳过配置文件中的规则 loader规则与内联互为补充module.rules让按扩展名自动处理成为默认行为内联请求则提供一次性、强制性的处理方式二者最终都汇入 lib/NormalModuleFactory.js 的请求解析与 lib/loaders/LoaderRunner.js 的执行管线。想继续深入可在本仓库中对照阅读声明类型 declarations/LoaderContext.d.ts、loader 运行时加载实现 lib/loaders/loadLoader.js以及更复杂的 loader 使用示例 examples/css样式表与 CSS Modules 的完整 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),仅供参考
返回列表