1. 首页

webpack原理解析(二)实现一个简单的Loader

Loader是什么

webpack中 loader 是一个非常重要的概念,loader 可以简单的理解成一个文件处理器,webpack使用 loader 来处理各类文件,比如 .scss转成成 css文件, 小图片转换成base64图片。

本质上讲,loader 只是导出为函数的 JavaScript 模块,webpack打包的时候,会调用这个函数,把上一个loader产生的结果或资源文件(resource file)传入进去。

这次我打算开发一个把普通图片转换成 .webp 格式的 Loader,以达到减小图片大小的目的。

Loader 工具库

开发 loader 之前,先要了解两个 loader开发的工具包——loader-utilsschema-utils

  • loader-utils 用于获取 loader 的 options
  • schema-utils 用于校验 loader 的 optionsJSON 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 开发准则

  • 单一职责:一个 loader 只处理一件事情,保证简单和可维护,复杂场景可以用多个 loader 组合完成。
  • 模块化:保证 loader 是模块化的。loader 生成模块需要遵循和普通模块一样的设计原则。
  • 无状态:在多次模块的转化之间,我们不应该在 loader 中保留状态。每个 loader 运行时应该确保与其他编译好的模块保持独立,同样也应该与前几个 loader 对相同模块的编译结果保持独立。

总之一句话,我们在写一个 loader 的时候,保持其职责单一,只需要关心输入和输出就好。

同步 loader 和 异步 loader

loader之所以有同步和异步之分,是因为有些资源的处理比较耗时,需要异步处理,等待处理完成后再继续执行。loader 用 this.callback() 返回处理结果,如下:


this.callback( err: Error | null, content: string | Buffer, sourceMap?: SourceMap, meta?: any ); 复制代码
  1. 第一个参数必须是 Error 或者 null
  2. 第二个参数是一个 string 或者 Buffer。
  3. 可选的:第三个参数必须是一个可以被这个模块解析的 source map。
  4. 可选的:第四个选项,会被 webpack 忽略,可以是任何东西(例如一些元数据)。

// 同步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 函数


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); }); }; 复制代码

开发自己的 Loader

项目目录结构

|–src
| |–cjs.js //commonJs 入口
| |–index.js // loader 主文件
| |–options.json // loader options 定义文件
|–babel.config.js
|–package.json
|–readme.md

定义 options

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 } 复制代码

编写 loader


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。

关注我们

webpack原理解析(二)实现一个简单的Loader

作者:快狗打车前端团队
链接:https://juejin.im/post/5e1fc8f9e51d4501fa46801b

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「画漫画的程序员」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:webpack原理解析(二)实现一个简单的Loader

链接:https://www.javascriptc.com/3939.html

« 打造工业级推荐系统(四):推荐系统怎么更好的帮助公司挣钱?
听说你还不懂React Hook?»
Flutter 中文教程资源

相关推荐

QR code