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

多loader时的执行顺序
loader是链式调用且执行顺序是从右到左,从后往前的
为什么是从后往前的执行顺序呢?
因为函数组合有两种情况,也可以使用下面讲到的自定义loader进行试验一下loader的执行顺序
loader的使用
之前的学习中我们试过这样使用loader,直接use数组,数组元素是使用的loader名称。webpack在解析的时候会引入对应的loader对资源进行解析。
这个use数组的元素除了是字符串之外,还可以直接是一个函数也就是一个loader,像这样可以简单地使用自定义的loader。
写一个loader
上面说的这种的自定义loader使用方法是正确的,但是在开发一个loader的过程中要怎么调试他呢,如果我在写一个loader,我每次想调试他的效果的时候我都需要将整个项目打包,这种调试方法无疑是高成本的。
实际上,我们更常用的是使用loader-runner高效进行loader的开发和调试。
loader-runner是什么?
提供一个运行loader的环境,也就是他封装了一些东西,可以直接独立使用,调试loader(对文件)的作用。
之所以允许不按照webpack就可以使用时因为webpack内部也是使用loader-runner去执行loader的。
loader-runner的官方说明:
import { runLoaders } from "loader-runner";runLoaders({resource: "/abs/path/to/file.txt?query",// String: Absolute path to the resource (optionally including query string)loaders: ["/abs/path/to/loader.js?query"],// String[]: Absolute paths to the loaders (optionally including query string)// {loader, options}[]: Absolute paths to the loaders with options objectcontext: { minimize: true },// Additional loader context which is used as base contextreadResource: fs.readFile.bind(fs)// A function to read the resource// Must have signature function(path, function(err, buffer))}, function(err, result) {// err: Error?// result.result: Buffer | String// The result// result.resourceBuffer: Buffer// The raw resource as Buffer (useful for SourceMaps)// result.cacheable: Bool// Is the result cacheable or do it require reexecution?// result.fileDependencies: String[]// An array of paths (files) on which the result depends on// result.contextDependencies: String[]// An array of paths (directories) on which the result depends on})
loader-runner的使用

开发一个raw-loader
raw-loader的功能就是将一个文件的内容变成string字符串
代码写了就不做笔记了
更加复杂的loader开发场景
loader的参数获取
通过loader-utils的getOptions方法获取
loader异常处理
- 同步处理异常
loader内直接通过throw抛出
在lodaer内使用this.callback函数

// 注释掉的是普通的loader返回方式。// return `export default ${json}`;throw new Error('error');// 这种返回方式更常用,第一个参数是Error对象,而且还可以返回多个值this.callback(null, json, 2, 3, 4);
- 异步
通过this.async返回一个异步函数,第一个参数是Error,第二个参数是处理的结果
这里亲测,如果使用异步逻辑通过同步方法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进行文件写入
还记得fileLoader有这个功能吗?来看一下fileLoader的源码:
const context = options.context || this.rootContext;const url = loaderUtils.interpolateName(this,options.name || '[contenthash].[ext]',{context,content,regExp: options.regExp,});let outputPath = url;if (options.outputPath) {if (typeof options.outputPath === 'function') {outputPath = options.outputPath(url, this.resourcePath, context);} else {outputPath = path.posix.join(options.outputPath, url);}}...if (typeof options.emitFile === 'undefined' || options.emitFile) {this.emitFile(outputPath, content);}
loaderUtils.interpolateName,这个也是loaderUtils很常用的一个API,主要是通过option.name指定的名字中的占位符,去生成最终的文件名。比如[contentHash][hash]这些(之所以要传入文件内容content就是因为这些hash值需要文件内容去做一个md5算法得出的哈希值),这个最终的文件名也就是输出的路径。作为输出的路径。
实战开发一个自动合成雪碧图的loader
啥是雪碧图?雪碧图是根据CSSsprite音译过来的,就是将很多很多的小图标放在一张图片上,称之为雪碧图。
支持的语法:
怎么将两张图片合成为一张图片

