概述
对于 loader,我们用了很多,熟悉的有 css-loader
、file-loader
等。但是 loader 的机制是什么,基本的 loader 结构是怎样的,如何搭建一个 loader 开发调试环境,如何自己编写一个 loader,异步 loader 怎么处理,怎么在 loader 中运用第三方 npm 包。本文将对以上问题进行讲解。
详述
loader 只是一个导出为函数的 JavaScript 模块。loader 是用来加载处理各种形式的资源,就像工厂生产线中的一道工序,输入资源,加工处理后再输出资源。
loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 import CSS 文件!
最简实现
1 | // demo-loader |
多 loader 的执行顺序
多个 loader 是链式调用,顺序是从后到前。例如下面示例:
1 | // webpack.config.js |
执行顺序是less-loader
> css-loader
> style-loader
。
loader-runner
webpack 的核心依赖包,用它来执行 loader。我们可以利用它在不用搭建完整 webpack 环境下进行 loader 的开发和调试。
使用方式:
1 | const { runLoaders } = require('loader-runner'); |
loader-utils
webpack loaders 的工具包,提供 getOptions
、parseQuery
等工具方法。使用示例如下:
1 | const loaderUtils = require('loader-utils'); |
同步 loader 与异步 loader
同步 loader 的返回值可以用 return
也可以用 this.callback()
,示例如下:
1 | // 同步方式一: |
1 | // 异步方式: |
同步this.callback()
异步callback()
的参数类型相同,如下:
1 | // callback的参数类型 |
loader 中使用缓存
webpack 中默认开启 loader 缓存,如果需要关闭缓存,可以如下设置:
1 | this.cacheable(false); |
编写 name-loader
实现将源数据中的 [name] 直接替换为 loader 选项中设置的 name。然后返回包含导出文本的 JavaScript 模块。
准备工作
目录结构
工具包安装
1 | npm i loader-runner loader-utils -S |
源文件 index.html
1 |
|
代码编写
name-loader
1 | // name-loader.js |
name-loader 调试脚本:
1 | // test-name-loader.js |
执行调试脚本:
1 | H:\workspace\webpack\my-loader>npm run test:name |
编写 sprite-loader
在上一个 loader 编写的环境中我们再开发一个 loader,实现自动将 css 中引用的小图片合并成雪碧图并生成新的 css 文件。
准备工作
目录结构:
spritesmith:
雪碧图合并功能选用第三方 node 模块spritesmith,生成雪碧图 Buffer 和坐标映射。使用示例如下:
1 | // Load in dependencies |
源文件 index.css
1 | /* src/sprite/index.css */ |
代码编写
sprite-loader
1 | // loaders/sprite-loader.js |
sprite-loader 调试脚本
1 | // src/test-sprite-loader.js |
执行调试脚本
1 | H:\workspace\webpack\my-loader>npm run test:sprite |
编写 loader 用法准则及注意事项
用法准则
- 简单易用。
- 使用链式传递。
- 模块化的输出。
- 确保无状态。
- 使用 loader utilities。
- 记录 loader 的依赖。
- 解析模块依赖关系。
- 提取通用代码。
- 避免绝对路径。
- 使用 peer dependencies。
注意事项
如果该 loader 是最终执行 loader(如 file-loader、style-loader 等),那么返回值应该是字符串,包含导出文本的 JavaScript 模块;如果该 loader 不是最终执行 loader(如 css-loader、sass-loader),那么返回值应该是 Buffer,将作为链式处理的 loader 的 source。
自定义 loader 的使用
只有一点需要注意,就是通过 resolveLoader
配置项指定 loader 的搜寻顺序。
使用示例
1 | const path = require('path'); |
效果展示
参考
https://webpack.docschina.org/contribute/writing-a-loader/
https://segmentfault.com/a/1190000014205729
《极客时间》