经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » webpack » 查看文章
详解Webpack loader 之 file-loader
来源:jb51  时间:2018/11/8 9:28:17  对本文有异议

简介

安装

  1. npm install --save-dev file-loader

用法

默认情况下,生成的文件的文件名就是文件内容的 MD5 哈希值并会保留所引用资源的原始扩展名。

  1. import img from './webpack-logo.png'

webpack.config.js

  1. module.exports = {
  2. module: {
  3. rules: [
  4. {
  5. test: /\.(png|jpg|gif)$/,
  6. use: [
  7. {
  8. loader: 'file-loader',
  9. options: {}
  10. }
  11. ]
  12. }
  13. ]
  14. }
  15. }

生成文件 bd62c377ad80f89061ea5ad8829df35b.png (默认的文件名为 [hash].[ext]),输出到输出目录并返回 public URL。

  1. "/public/path/bd62c377ad80f89061ea5ad8829df35b.png"

当然如果不想使用默认的文件名,我们也可以通过配置 options.name 选项来设置输出的文件名命名规则,需要注意的是 name 选项支持的类型为: {String|Function}

String 类型

webpack.config.js

  1. {
  2. loader: 'file-loader',
  3. options: {
  4. name: '[path][name].[ext]'
  5. }
  6. }

Function 类型

webpack.config.js

  1. {
  2. loader: 'file-loader',
  3. options: {
  4. name (file) {
  5. if (env === 'development') {
  6. return '[path][name].[ext]'
  7. }
  8. return '[hash].[ext]'
  9. }
  10. }
  11. }

以上的示例中,我们使用了 [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. {
  2. loader: 'file-loader',
  3. options: {
  4. name: '[path][name].[ext]',
  5. outputPath: 'images/'
  6. }
  7. }

需要注意的是,outputPath 所设置的路径,是相对于 webpack 的输出目录。

publicPath

publicPath 用于配置自定义 public 发布目录,支持 String|Function 类型,默认值为 __webpack_public__path__ ,用法如下:

webpack.config.js

  1. {
  2. loader: 'file-loader',
  3. options: {
  4. name: '[path][name].[ext]',
  5. publicPath: 'assets/'
  6. }
  7. }

emitFile

emitFile 用于设置是否生成文件,类型是 Boolean,默认值为 true。但我们可以通过将 emitFile 设置为 false 来禁用该默认行为。

webpack.config.js

  1. {
  2. loader: 'file-loader',
  3. options: {
  4. emitFile: false
  5. }
  6. }

outputPath vs publicPath

outputPath 仅仅告诉 webpack 结果存储在哪里,然而 publicPath 选项则被许多 webpack 的插件用于在生产模式下更新内嵌到 css、html 文件内的 url 值。例如:

  1. // Development: Both Server and the image are on localhost
  2. .image {
  3. background-image: url('./test.png');
  4. }
  5. // Production: Server is on Heroku but the image is on a CDN
  6. .image {
  7. background-image: url('https://some-cdn/test.png');
  8. }

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';
  2.  
  3. import loaderUtils from 'loader-utils';
  4. import validateOptions from 'schema-utils';
  5.  
  6. import schema from './options.json';

获取配置对象及验证

  1. export default function loader(content) {
  2. if (!this.emitFile)
  3. throw new Error('File Loader\n\nemitFile is required from module system');
  4.  
  5. const options = loaderUtils.getOptions(this) || {};
  6.  
  7. validateOptions(schema, options, 'File Loader');
  8. }

以上代码中,emitFile 是由 loader 上下文提供的方法,用于输出一个文件,对应的函数签名如下:

  1. emitFile(name: string, content: Buffer|string, sourceMap: {...})

在调用 file-loader 时,如果发现 this.emitFile 无效,则会抛出异常。接着 file-loader 会先调用 loaderUtils.getOptions() 方法,获取当前 loader 对应的配置对象,然后基于已定义的 Schema,验证配置对象的有效性。对应的 Schema 定义如下(不包含异常提示信息):

  1. {
  2. "type": "object",
  3. "properties": {
  4. "name": {},
  5. "regExp": {},
  6. "context": {
  7. "type": "string"
  8. },
  9. "publicPath": {},
  10. "outputPath": {},
  11. "useRelativePath": {
  12. "type": "boolean"
  13. },
  14. "emitFile": {
  15. "type": "boolean"
  16. }
  17. },
  18. "additionalProperties": true
  19. }

获取 context 及生成文件名称

  1. const context =
  2. options.context //自定义文件context
  3. // 从webpack 4开始,原先的this.options.context
  4. // 被改进为this.rootContext
  5. || this.rootContext ||
  6. (this.options && this.options.context);
  7.  
  8. const url = loaderUtils.interpolateName(
  9. this,
  10. options.name, // 默认为"[hash].[ext]"
  11. {
  12. context,
  13. content,
  14. regExp: options.regExp,
  15. });

loaderUtils 中的 interpolateName 方法,用于生成对应的文件名,该方法的签名如下:

  1. interpolateName(loaderContext, name, options);

其中 loaderContext 为 loader 的上下文对象,name 为文件名称模板,options 为配置对象,支持 context,content 和 regExp 属性。该方法的使用示例如下:

示例一:

  1. // loaderContext.resourcePath = "/app/js/javascript.js";
  2. let interpolatedName = loaderUtils.interpolateName(
  3. loaderContext,
  4. "js/[hash].script.[ext]",
  5. {
  6. content: "console.log('loaderUtils')"
  7. });
  8. // => js/e353f4da4c3e380646d2b4d75c8a13ab.script.js

以上示例核心的处理流程如下:

示例二:

  1. // loaderContext.resourcePath = "/app/js/page-home.js"
  2. loaderUtils.interpolateName(
  3. loaderContext,
  4. "script-[1].[ext]",
  5. {
  6. regExp: "page-(.*)\\.js",
  7. content: "console.log('loaderUtils')"
  8. });
  9. // => script-home.js

处理 outputPath

  1. let outputPath = url;
  2.  
  3. if (options.outputPath) {
  4. if (typeof options.outputPath === 'function') {
  5. outputPath = options.outputPath(url);
  6. } else {
  7. outputPath = path.posix.join(options.outputPath, url);
  8. }
  9. }

处理 publicPath

  1. // __webpack_require__.p = "";
  2. let publicPath = `__webpack_public_path__ + ${JSON.stringify(outputPath)}`;
  3.  
  4. if (options.publicPath) {
  5. if (typeof options.publicPath === 'function') {
  6. publicPath = options.publicPath(url);
  7. } else if (options.publicPath.endsWith('/')) {
  8. publicPath = options.publicPath + url;
  9. } else {
  10. publicPath = `${options.publicPath}/${url}`;
  11. }
  12.  
  13. publicPath = JSON.stringify(publicPath);
  14. }

处理 emitFile

  1. if (options.emitFile === undefined || options.emitFile) {
  2. // 把文件输出到指定的outputPath路径
  3. this.emitFile(outputPath, content);
  4. }

导出最终路径

  1. return `module.exports = ${publicPath};`;

参考资源

loader API
webpack-the-confusing-parts

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持w3xue。

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号