欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

详解Webpack loader 之 file-loader

程序员文章站 2022-09-24 13:30:56
简介 安装 npm install --save-dev file-loader 用法 默认情况下,生成的文件的文件名就是文件内容的 md5 哈希值并会...

简介

安装

npm install --save-dev file-loader

用法

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

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

webpack.config.js

module.exports = {
 module: {
  rules: [
   {
    test: /\.(png|jpg|gif)$/,
    use: [
     {
      loader: 'file-loader',
      options: {}
     }
    ]
   }
  ]
 }
}

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

"/public/path/bd62c377ad80f89061ea5ad8829df35b.png"

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

string 类型

webpack.config.js

{
 loader: 'file-loader',
 options: {
  name: '[path][name].[ext]'
 }
}

function 类型

webpack.config.js

{
 loader: 'file-loader',
 options: {
  name (file) {
   if (env === 'development') {
    return '[path][name].[ext]'
   }
   return '[hash].[ext]'
  }
 }
}

以上的示例中,我们使用了 [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>],对应的说明如下:

详解Webpack loader 之 file-loader

其实除了以上常用的四个占位符之外,还有支持 [n] ,n 是数值类型,表示当前文件名按照查询参数 regexp 匹配后获得到第 n 个匹配结果。介绍完 name 配置项,接下来我们来继续介绍几个常用的配置。

常用配置项 outputpath

outputpath 用于配置自定义 output 输出目录,支持 string|function 类型,默认值为 ‘undefined',用法如下:

webpack.config.js

{
 loader: 'file-loader',
 options: {
  name: '[path][name].[ext]',
  outputpath: 'images/'
 }
}

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

publicpath

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

webpack.config.js

{
 loader: 'file-loader',
 options: {
  name: '[path][name].[ext]',
  publicpath: 'assets/'
 }
}

emitfile

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

webpack.config.js

{
 loader: 'file-loader',
 options: {
  emitfile: false
 }
}

outputpath vs publicpath

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

// development: both server and the image are on localhost
.image { 
 background-image: url('./test.png');
 }
 
// production: server is on heroku but the image is on a cdn
.image { 
 background-image: url('https://some-cdn/test.png');
 }

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 的部分源码。

导入依赖模块

import path from 'path';

import loaderutils from 'loader-utils';
import validateoptions from 'schema-utils';

import schema from './options.json';

获取配置对象及验证

export default function loader(content) {
 if (!this.emitfile)
  throw new error('file loader\n\nemitfile is required from module system');

 const options = loaderutils.getoptions(this) || {};

 validateoptions(schema, options, 'file loader');
}

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

emitfile(name: string, content: buffer|string, sourcemap: {...})

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

{
 "type": "object",
 "properties": {
  "name": {},
  "regexp": {},
  "context": {
   "type": "string"
  },
  "publicpath": {},
  "outputpath": {},
  "userelativepath": {
   "type": "boolean"
  },
  "emitfile": {
   "type": "boolean"
  }
 },
 "additionalproperties": true
}

获取 context 及生成文件名称

const context = 
  options.context //自定义文件context
  // 从webpack 4开始,原先的this.options.context
  // 被改进为this.rootcontext
  || this.rootcontext || 
  (this.options && this.options.context);

const url = loaderutils.interpolatename(
 this, 
 options.name, // 默认为"[hash].[ext]"
 {
  context,
  content,
  regexp: options.regexp,
});

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

interpolatename(loadercontext, name, options);

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

示例一:

// loadercontext.resourcepath = "/app/js/javascript.js";
let interpolatedname = loaderutils.interpolatename(
 loadercontext, 
 "js/[hash].script.[ext]", 
 {
   content: "console.log('loaderutils')"
 });
// => js/e353f4da4c3e380646d2b4d75c8a13ab.script.js

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

详解Webpack loader 之 file-loader

示例二:

// loadercontext.resourcepath = "/app/js/page-home.js"
loaderutils.interpolatename(
 loadercontext, 
 "script-[1].[ext]", 
 { 
 regexp: "page-(.*)\\.js", 
 content: "console.log('loaderutils')"
 });
// => script-home.js

处理 outputpath

let outputpath = url;

if (options.outputpath) {
  if (typeof options.outputpath === 'function') {
   outputpath = options.outputpath(url);
  } else {
   outputpath = path.posix.join(options.outputpath, url);
  }
}

处理 publicpath

// __webpack_require__.p = "";
let publicpath = `__webpack_public_path__ + ${json.stringify(outputpath)}`;

if (options.publicpath) {
  if (typeof options.publicpath === 'function') {
   publicpath = options.publicpath(url);
  } else if (options.publicpath.endswith('/')) {
   publicpath = options.publicpath + url;
  } else {
   publicpath = `${options.publicpath}/${url}`;
  }

  publicpath = json.stringify(publicpath);
}

处理 emitfile

if (options.emitfile === undefined || options.emitfile) {
  // 把文件输出到指定的outputpath路径
  this.emitfile(outputpath, content); 
}

导出最终路径

return `module.exports = ${publicpath};`;

参考资源

loader api

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