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

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: webpack中 loader 是一个非常重要的概念,loader 可以简单的理解成一个文件处理器,webpack使用 loader 来处理各类文件,比如 .scss转成成 css文件, 小图片转换成base64图片。

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


  1. 第二个参数是一个 string 或者 Buffer。


  1. 可选的:第三个参数必须是一个可以被这个模块解析的 source map。


  1. 可选的:第四个选项,会被 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。


相关文章
|
1月前
|
存储 算法 Java
解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用
在Java中,Set接口以其独特的“无重复”特性脱颖而出。本文通过解析HashSet的工作原理,揭示Set如何利用哈希算法和equals()方法确保元素唯一性,并通过示例代码展示了其“无重复”特性的具体应用。
41 3
|
21天前
|
监控 前端开发 JavaScript
Webpack 中 HMR 插件的工作原理
【10月更文挑战第23天】可以进一步深入探讨 HMR 工作原理的具体细节、不同场景下的应用案例,以及与其他相关技术的结合应用等方面的内容。通过全面、系统地了解 HMR 插件的工作原理,能够更好地利用这一功能,为项目的成功开发提供有力保障。同时,要不断关注技术的发展动态,以便及时掌握最新的 HMR 技术和最佳实践。
|
21天前
|
缓存 前端开发 JavaScript
Webpack 动态加载的原理
【10月更文挑战第23天】Webpack 动态加载通过巧妙的机制和策略,实现了模块的按需加载和高效运行,提升了应用程序的性能和用户体验。同时,它也为前端开发提供了更大的灵活性和可扩展性,适应了不断变化的业务需求和技术发展。
|
19天前
|
算法 Java 数据库连接
Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性
本文详细介绍了Java连接池技术,从基础概念出发,解析了连接池的工作原理及其重要性。连接池通过复用数据库连接,显著提升了应用的性能和稳定性。文章还展示了使用HikariCP连接池的示例代码,帮助读者更好地理解和应用这一技术。
32 1
|
21天前
|
缓存 前端开发 JavaScript
webpack 原理
【10月更文挑战第23天】Webpack 原理是一个复杂但又非常重要的体系。它通过模块解析、依赖管理、加载器和插件的协作,实现了对各种模块的高效打包和处理,为现代前端项目的开发和部署提供了强大的支持。同时,通过代码分割、按需加载、热模块替换等功能,提升了应用程序的性能和用户体验。随着前端技术的不断发展,Webpack 也在不断演进和完善,以适应不断变化的需求和挑战。
|
24天前
|
数据采集 存储 编解码
一份简明的 Base64 原理解析
Base64 编码器的原理,其实很简单,花一点点时间学会它,你就又消除了一个知识盲点。
66 3
|
6天前
|
存储 供应链 物联网
深入解析区块链技术的核心原理与应用前景
深入解析区块链技术的核心原理与应用前景
|
6天前
|
存储 供应链 安全
深度解析区块链技术的核心原理与应用前景
深度解析区块链技术的核心原理与应用前景
15 0
|
1月前
|
缓存 前端开发 JavaScript
Webpack 打包的基本原理
【10月更文挑战第5天】
|
1月前
|
前端开发 UED
Webpack 中处理 CSS 和图片资源的多 Loader 配置
【10月更文挑战第12天】 处理 CSS 和图片资源是 Webpack 配置中的重要部分。通过合理选择和配置多个 Loader,可以实现对这些资源的精细处理和优化,提升项目的性能和用户体验。在实际应用中,需要不断探索和实践,根据项目的具体情况进行灵活调整和优化,以达到最佳的处理效果。通过对 Webpack 中多 Loader 处理 CSS 和图片资源的深入了解和掌握,你将能够更好地应对各种复杂的资源处理需求,为项目的成功构建和运行提供坚实的基础。
55 1

推荐镜像

更多