Webpack loader 之 file-loader
本文于852天之前发表,文中内容可能已经过时。
简介
安装
1 | npm install --save-dev file-loader |
用法
默认情况下,生成的文件的文件名就是文件内容的 MD5 哈希值并会保留所引用资源的原始扩展名。
1 | import img from './webpack-logo.png' |
webpack.config.js
1 | module.exports = { |
生成文件 bd62c377ad80f89061ea5ad8829df35b.png
(默认的文件名为 [hash].[ext]),输出到输出目录并返回 public URL。
1 | "/public/path/bd62c377ad80f89061ea5ad8829df35b.png" |
当然如果不想使用默认的文件名,我们也可以通过配置 options.name
选项来设置输出的文件名命名规则,需要注意的是 name 选项支持的类型为:{String|Function}
:
- String 类型
webpack.config.js
1 | { |
- Function 类型
webpack.config.js
1 | { |
以上的示例中,我们使用了 [path]
,[name]
, [hash]
和 [ext]
占位符,它们对应的含义是:
- [ext]:String,默认值为 file.extname,表示资源扩展名;
- [name]:String,默认值为 file.basename,表示资源的基本名称;
- [path]:String,默认值为 file.dirname,表示资源相对于 context 的路径;
- [hash]:String,默认值为 md5,内容的哈希值,支持灵活的 hashes 配置,配置规则为:
[<hashType>:hash:<digestType>:<length>]
,对应的说明如下:
其实除了以上常用的四个占位符之外,还有支持 [N]
,N 是数值类型,表示当前文件名按照查询参数 regExp
匹配后获得到第 N 个匹配结果。介绍完 name 配置项,接下来我们来继续介绍几个常用的配置。
常用配置项
outputPath
outputPath 用于配置自定义 output
输出目录,支持 String|Function 类型,默认值为 ‘undefined’,用法如下:
webpack.config.js
1 | { |
需要注意的是,outputPath 所设置的路径,是相对于 webpack 的输出目录。
publicPath
publicPath 用于配置自定义 public
发布目录,支持 String|Function 类型,默认值为 __webpack_public__path__
,用法如下:
webpack.config.js
1 | { |
emitFile
emitFile 用于设置是否生成文件,类型是 Boolean,默认值为 true。但我们可以通过将 emitFile 设置为 false 来禁用该默认行为。
webpack.config.js
1 | { |
outputPath vs publicPath
outputPath 仅仅告诉 webpack 结果存储在哪里,然而 publicPath 选项则被许多 webpack 的插件用于在生产模式下更新内嵌到 css、html 文件内的 url 值。例如:
1 | // Development: Both Server and the image are on localhost |
loader 准则
编写 loader 时应该遵循以下准则:
- 简单易用。
- 使用链式传递。
- 模块化的输出。
- 确保无状态。
- 使用 loader utilities。
- 记录 loader 的依赖。
- 解析模块依赖关系。
- 提取通用代码。
- 避免绝对路径。
- 使用 peer dependencies。
以上的准则按重要程度排序,但某些仅适用于某些场景。若想进一步了解自定义 loader,可以阅读 编写一个 loader 这个文档。接下来,我们来基于上述的准则分析一下 file-loader 的源码。
file-loader 源码简析
所谓 loader 只是一个导出为函数对象的 JavaScript 模块。loader runner 会调用这个函数,然后把上一个 loader 产生的结果或者资源文件传入进去。函数的 this
上下文将由 webpack 填充,并且 loader runner 具有一些有用方法,可以使 loader 改变为异步调用方式,或者获取 query 参数。
其实本文介绍的 file-loader 并不会对文件的内容进行任何转换,只是复制一份文件内容,并根据相关的配置生成对应的文件名,所生成的文件名一般会带上 hash 值,从而避免文件重名导致冲突。接下来我们来简单分析一下 file-loader 的部分源码。
导入依赖模块
1 | import path from 'path'; |
获取配置对象及验证
1 | export default function loader(content) { |
以上代码中,emitFile 是由 loader 上下文提供的方法,用于输出一个文件,对应的函数签名如下:
1 | emitFile(name: string, content: Buffer|string, sourceMap: {...}) |
在调用 file-loader 时,如果发现 this.emitFile 无效,则会抛出异常。接着 file-loader 会先调用 loaderUtils.getOptions() 方法,获取当前 loader 对应的配置对象,然后基于已定义的 Schema,验证配置对象的有效性。对应的 Schema 定义如下(不包含异常提示信息):
1 | { |
获取 context 及生成文件名称
1 | const context = |
loaderUtils 中的 interpolateName 方法,用于生成对应的文件名,该方法的签名如下:
1 | interpolateName(loaderContext, name, options); |
其中 loaderContext 为 loader 的上下文对象,name 为文件名称模板,options 为配置对象,支持 context,content 和 regExp 属性。该方法的使用示例如下:
示例一:
1 | // loaderContext.resourcePath = "/app/js/javascript.js"; |
以上示例核心的处理流程如下:
示例二:
1 | // loaderContext.resourcePath = "/app/js/page-home.js" |
处理 outputPath
1 | let outputPath = url; |
处理 publicPath
1 | // __webpack_require__.p = ""; |
处理 emitFile
1 | if (options.emitFile === undefined || options.emitFile) { |
导出最终路径
1 | return `module.exports = ${publicPath};`; |
参考资源
欢迎小伙伴们订阅全栈修仙之路,及时阅读 TypeScript、Node/Deno、Angular 技术栈最新文章。
