webpack中 loader
是一个很是重要的概念,loader 能够简单的理解成一个文件处理器,webpack使用 loader
来处理各种文件,好比 .scss
转成成 css
文件, 小图片转换成base64图片。css
本质上讲,loader
只是导出为函数的 JavaScript 模块,webpack打包的时候,会调用这个函数,把上一个loader产生的结果或资源文件(resource file)传入进去。webpack
此次我打算开发一个把普通图片转换成 .webp
格式的 Loader,以达到减少图片大小的目的。web
开发 loader 以前,先要了解两个 loader开发的工具包——loader-utils
和 schema-utils
npm
loader-utils
用于获取 loader 的 options
。schema-utils
用于校验 loader 的 options
与 JSON Schema
结构定义的是否一致。import { getOptions } from 'loader-utils';
import { validateOptions } from 'schema-utils';
const schema = {
// ...
}
export default function(source) {
// 获取 options
const options = getOptions(this);
// 检验loader的options是否合法
validateOptions(schema, options, 'Demo Loader');
// 在这里写转换 loader 的逻辑
// ...
};
复制代码
总之一句话,咱们在写一个 loader 的时候,保持其职责单一,只须要关心输入和输出就好。json
loader之因此有同步和异步之分,是由于有些资源的处理比较耗时,须要异步处理,等待处理完成后再继续执行。loader 用 this.callback()
返回处理结果,以下:babel
this.callback(
err: Error | null,
content: string | Buffer,
sourceMap?: SourceMap,
meta?: any
);
复制代码
// 同步loader 返回多个处理结果
module.exports = function(content, map, meta) {
this.callback(null, someSyncOperation(content), map, meta);
};
复制代码
同步模式下,若是返回结果只有一个,也能够直接使用 return
返回结果。异步
// 同步loader 只返回一个处理结果
module.exports = function(content, map, meta) {
return someSyncOperation(content);
};
复制代码
异步模式下使用 this.async
来获取 callback
函数async
module.exports = function(content, map, meta) {
var callback = this.async();
someAsyncOperation(content, function(err, result, sourceMaps, meta) {
if (err) return callback(err);
callback(null, result, sourceMaps, meta);
});
};
复制代码
|--src
| |--cjs.js //commonJs 入口
| |--index.js // loader 主文件
| |--options.json // loader options 定义文件
|--babel.config.js
|--package.json
|--readme.md模块化
options
里只有一个属性 quality
, 用来控制生成 .webp
文件的质量,定义以下:函数
{
"additionalProperties": true,
"properties": {
"quality": {
"description": "quality factor (0:small..100:big), default=75",
"type": "number"
}
},
"type": "object"
}
复制代码
cwebp
这个js模块提供了普通图片和.webp
之间相互转换的功能。
const CWebp = require('cwebp').CWebp
/** * 普通图片转 .webp图片 * @param {string | buffer} img 图片绝对路径或二进制流 * @param {number} quality 生成 webp 图片的质量,默认75 * @returns .webp 文件流 */
async function convertToWebp (img, quality = 75) {
let encoder = new CWebp(img)
encoder.quality = quality
let buffer = await encoder.toBuffer()
return buffer
}
复制代码
import schema from './options.json'
export default async function loader (content) {
// 异步模式
let callback = this.async()
// 获取 options
const options = loaderUtils.getOptions(this) || {}
// 验证 options
validateOptions(schema, options, {
name: 'webp Loader',
baseDataPath: 'options'
})
try {
// 普通图片转 .webp
let buffer = await convertToWebp(content, options.quality)
callback(null, buffer)
} catch (err) {
callback(err)
}
}
// loader 接收文件流
export const raw = true
复制代码
到这里这个 Loader 就基本完成了。
// webpack.config.js
module.exports = {
// 其余配置
// ...
module: {
rules: [{
test: /\.(png|jpg|gif)$/,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext].webp'
}
},{
loader: 'webp-loader',
options: {
quality: 70
}
}]
}]
}
}
复制代码
其实写一个 Loader 并无想象中的那么复杂,只要掌握了基本要点,你也能够拥有本身的 Loader。