让我们回忆一下之前学习webpack 使用的时候学习的一些loader:less-loader orderpreFix-loader file-loader url-loader。

简单介绍

loader的定义:

image.png

多loader时的执行顺序

loader是链式调用且执行顺序是从右到左,从后往前的
image.png

为什么是从后往前的执行顺序呢?

因为函数组合有两种情况,也可以使用下面讲到的自定义loader进行试验一下loader的执行顺序
image.png

loader的使用

之前的学习中我们试过这样使用loader,直接use数组,数组元素是使用的loader名称。webpack在解析的时候会引入对应的loader对资源进行解析。
image.png

这个use数组的元素除了是字符串之外,还可以直接是一个函数也就是一个loader,像这样可以简单地使用自定义的loader。
image.png

写一个loader

上面说的这种的自定义loader使用方法是正确的,但是在开发一个loader的过程中要怎么调试他呢,如果我在写一个loader,我每次想调试他的效果的时候我都需要将整个项目打包,这种调试方法无疑是高成本的。
实际上,我们更常用的是使用loader-runner高效进行loader的开发和调试

loader-runner是什么?

提供一个运行loader的环境,也就是他封装了一些东西,可以直接独立使用,调试loader(对文件)的作用。
之所以允许不按照webpack就可以使用时因为webpack内部也是使用loader-runner去执行loader的。
image.png

loader-runner的官方说明:

  1. import { runLoaders } from "loader-runner";
  2. runLoaders({
  3. resource: "/abs/path/to/file.txt?query",
  4. // String: Absolute path to the resource (optionally including query string)
  5. loaders: ["/abs/path/to/loader.js?query"],
  6. // String[]: Absolute paths to the loaders (optionally including query string)
  7. // {loader, options}[]: Absolute paths to the loaders with options object
  8. context: { minimize: true },
  9. // Additional loader context which is used as base context
  10. readResource: fs.readFile.bind(fs)
  11. // A function to read the resource
  12. // Must have signature function(path, function(err, buffer))
  13. }, function(err, result) {
  14. // err: Error?
  15. // result.result: Buffer | String
  16. // The result
  17. // result.resourceBuffer: Buffer
  18. // The raw resource as Buffer (useful for SourceMaps)
  19. // result.cacheable: Bool
  20. // Is the result cacheable or do it require reexecution?
  21. // result.fileDependencies: String[]
  22. // An array of paths (files) on which the result depends on
  23. // result.contextDependencies: String[]
  24. // An array of paths (directories) on which the result depends on
  25. })

loader-runner的使用

image.png

开发一个raw-loader

raw-loader的功能就是将一个文件的内容变成string字符串
image.png

代码写了就不做笔记了

更加复杂的loader开发场景

loader的参数获取

通过loader-utils的getOptions方法获取
image.png

loader异常处理

  • 同步处理异常
  1. loader内直接通过throw抛出

  2. 在lodaer内使用this.callback函数

image.png

  1. // 注释掉的是普通的loader返回方式。
  2. // return `export default ${json}`;
  3. throw new Error('error');
  4. // 这种返回方式更常用,第一个参数是Error对象,而且还可以返回多个值
  5. this.callback(null, json, 2, 3, 4);
  • 异步

通过this.async返回一个异步函数,第一个参数是Error,第二个参数是处理的结果
image.png
这里亲测,如果使用异步逻辑通过同步方法callback返回会报错:callback is not defined。我猜想他的逻辑应该是执行完loader函数之后,通过观察者模式的设计方式得知callback,async哪个被调用了,而且其中一个callback函数被调用之后,另一个再调用会报错。
如果是async则使用await等待异步动作完成之后,再进行返回loader的结果。如果是callback,那么就执行callback和返回。如果都不是,那么就返回loader函数的返回值。

loader的缓存

webpack中默认开启loader缓存,但也可以使用this.cacheable(false)关闭缓存。

缓存条件:loader的结果在相同输入下有确定的输出,因此 有依赖的loader不应该使用缓存

loader中进行文件输出

通过this.emitFile进行文件写入
image.png

还记得fileLoader有这个功能吗?来看一下fileLoader的源码:

  1. const context = options.context || this.rootContext;
  2. const url = loaderUtils.interpolateName(
  3. this,
  4. options.name || '[contenthash].[ext]',
  5. {
  6. context,
  7. content,
  8. regExp: options.regExp,
  9. }
  10. );
  11. let outputPath = url;
  12. if (options.outputPath) {
  13. if (typeof options.outputPath === 'function') {
  14. outputPath = options.outputPath(url, this.resourcePath, context);
  15. } else {
  16. outputPath = path.posix.join(options.outputPath, url);
  17. }
  18. }
  19. ...
  20. if (typeof options.emitFile === 'undefined' || options.emitFile) {
  21. this.emitFile(outputPath, content);
  22. }

loaderUtils.interpolateName,这个也是loaderUtils很常用的一个API,主要是通过option.name指定的名字中的占位符,去生成最终的文件名。比如[contentHash][hash]这些(之所以要传入文件内容content就是因为这些hash值需要文件内容去做一个md5算法得出的哈希值),这个最终的文件名也就是输出的路径。作为输出的路径。

实战开发一个自动合成雪碧图的loader

啥是雪碧图?雪碧图是根据CSSsprite音译过来的,就是将很多很多的小图标放在一张图片上,称之为雪碧图。

支持的语法:
image.png

怎么将两张图片合成为一张图片

image.png