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。