Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >[ webpack ] webpack 的 loader 和 plugin 开发的方法

[ webpack ] webpack 的 loader 和 plugin 开发的方法

原创
作者头像
GavinUI
修改于 2021-06-12 08:26:02
修改于 2021-06-12 08:26:02
8300
举报
文章被收录于专栏:GavinUIGavinUI

虽说不同构建工具在原理上是大同小异,但下一篇也应该写写 vite 了。

Loader 和 Plugins 的区别

loader 主要的是处理静态资源,而 plugins 是可以贯穿在整个 webpack 构建的周期中,他能做到 loader 做不到的事情。但是,loader 他可以用独立的运行环境,可以在本地使用一些库进行本地发发调制,而 plugins 不行,他必须编写好这个 plugin 之后在 webpack 构建中将 plugin 放在 plugins 的数组中执行。

loader 开发的常用的开发方法

webpack 文档 DEMO 结构分析

代码语言:txt
AI代码解释
复制
import { getOptions } from 'loader-utils';
import { validate } from 'schema-utils';
const schema = {
  type: 'object',
  properties: {
    test: {
      type: 'string',
    },
  },
};
export default function (source) {
  const options = getOptions(this);
  validate(schema, options, {
    name: 'Example Loader',
    baseDataPath: 'options',
  });
  // Apply some transformations to the source...
  return `export default ${JSON.stringify(source)}`;
}

loader 实际上就是一个 js 模块,提供一个方法对原文件进行逻辑操作,处理完毕之后返回回去的一个过程。顺带提一点就是,loader 的链式调用是从后往前。

同步 loader 参数获取

参数获取可以使用一个 叫 loader-utils 的 loader,使用其中的 getOptions 的方法就可以拿到传递的参数。

在 runLoaders 配置中 loaders 参照文档修改为带 options 的配置,举例加上一个对象:

代码语言:txt
AI代码解释
复制
runLoaders({
    loaders: [
        // path.join(__dirname, './loaders/raw-loader.js'),
        {
            loader: path.join(__dirname, './loaders/raw-loader.js'),
            options:{
                env: 'development',
                version: '1.0.0'
            },
        }
    ],
...})

之后使用 loader-utils 将传递的参数带出。注意,这里的 module.exports 不能使用箭头函数,否则,this 是指向了当前的作用域,就拿不到 runLoaders 里面的属性了。

代码语言:txt
AI代码解释
复制
// npm install loader-utils -save-dev
const loaderUtils = require('loader-utils');

module.exports = function (source) {
    console.log(this);
    const { env, version } = loaderUtils.getOptions(this);
    console.log(env);
    console.log(version);
    ...
};

// npmjs -> https://www.npmjs.com/package/loader-utils 

同步 loader 异常处理

同步执行的情况下,处理异常错误打印的方式有两种:

第一种:是直接使用 Error 输出错误,可以在信息内填写错误编号

代码语言:txt
AI代码解释
复制
module.exports = function (source) {
 	  ...
    return new Error('error:10001');
};

第二种:是直接使用 this.callback ,根据参数的不同,再执行下一步或者是输出报错信息。

代码语言:txt
AI代码解释
复制
module.exports = function (source) {
		...
    return this.callback(new Error('error:10001'), source);
};

this.callback 的第一个参数是 Error 时,表示异常直接报错,如果第一个参数是 null ,那就说明函数正常执行下一步返回数据, 同时也支持多个参数进行传递,代码如下:

代码语言:txt
AI代码解释
复制
module.exports = function (source) {
		...
		const params = { key: 1 }
    return this.callback(null, source, params);
};

异步 loader 处理

使用异步获取结果需要使用 this.async() 这个方法,作为一个 callback 使用,第一个参数是判断是否错误,第二个直接传递参数。

代码语言:txt
AI代码解释
复制
module.exports = function (source) {
		...
    const callback = this.async();
    fs.readFile(path.join(__dirname, '../src/number.txt'), 'utf-8', (err, data) => {
        if(err){
            callback(err,'error')
        }
        callback(null, data);
    });
};

loader 输出文件

使用 emitFile 进行输出。

代码语言:txt
AI代码解释
复制
const loaderUtils = require('loader-utils');
module.exports = function (source) {
    const url = loaderUtils.interpolateName(this, '[name].[ext]', source);
    this.emitFile(url, source)
    return source;
}

开发适用于活动的 CSS Sprites loader

首先,需要使用 spritesmith 这个依赖,将多张图片和合并到一起。

第一步:读取图片 URL

思路是先将 css 文件获取到,再使用正则匹配导出所有的图片地址

代码语言:txt
AI代码解释
复制
const loaderUtils = require('loader-utils');
module.exports = function (source) {
   const images = source.match(/url\((\S*)/g);
   const matchedImages = [];
   if (images && images.length > 0) {
		for (let i = 0; i < images.length; i++) {
     const img = images[i].match(/url\(..(\S*)\'/)[1];
     matchedImages.push(path.join(__dirname, img));
    }
   }
}

再把文件的目录提取出来,转换成一个绝对地址 push 到一个数组里面,打印出来的 matchedImages 如下:

代码语言:txt
AI代码解释
复制
[
  '/Users/../images/web-security-bg-1.jpeg',
  '/Users/../images/webpack-images-2.png'
]
第二步:使用 spritesmith 合并图片和 css 地址替换
代码语言:txt
AI代码解释
复制
Spritesmith.run({ src: matchedImages}, (err, result) => {
	fs.writeFileSync(path.join(process.cwd(), 'dist/sprite.png'), result.image);
	source = source.replace(/url\((\S*)/g, (match) =>{
		return `url("dist/sprite.jpg")`;
  });
  fs.writeFileSync(path.join(process.cwd(), 'dist/index.css'), source);
     callback(null,source); })

在 dist 的目录中,就会出现一个合并好的图片同时 dist 里面还有一个已替换了 sprite 图的 css 文件 ,当然这里只是说明了一个思路,如果要完全的实现图片和样式的替换还需要考虑到背景大小,定位或者是一些边界问题,这里就不再细说了。

webpack 的 Plugin 的常用的开发方法

webpack 文档 DEMO 结构分析

代码语言:txt
AI代码解释
复制
// webpack writting a plugin -> https://webpack.js.org/contribute/writing-a-plugin/

// A JavaScript class.
class HelloWorldPlugin {
  apply(compiler) {
    compiler.hooks.done.tap('Hello World Plugin', (
      stats /* stats is passed as an argument when done hook is tapped.  */
    ) => {
      console.log('Hello World!');
    });
  }
}
module.exports = HelloWorldPlugin;

plugin 实际上一个一个类,在 webpack 使用 plugin 的时候都会有 new XXplugin() 的操作,就是新建一个 plugin 的类。apply 是 plugin 在 webpack 是每一次构建的时候都会运行。

hooks 是 compiler 对象的一个钩子,也可以说是可以监听在某个阶段做一些什么样的事情。会掉的最后就是这个 plugin 的逻辑代码。

开发一个压缩构建资源为 zip 包的 plugin

创建 zip 文件

首先,还是先使用一个 jszip 它可以将文件压缩成一个 zip 包,使用 compiler 对象的 hooks 的 emit 钩子,生成一个文件。

代码语言:txt
AI代码解释
复制
const JSZip = require('jszip');
const zip = new JSZip();
compiler.hooks.emit.tapAsync('ZipPlugin', (Compilation, callback) => {
    const folder = zip.folder(this.options.filename);
    for (let filename in Compilation.assets) {
        const source = Compilation.assets[filename].source();
        folder.file(filename, source)
    }
})

使用 zip.folder 先设置 zip 包的名称, 在触发到了emit 的 tapAsunc 异步钩子的时候,处理 compilation 的内容填充到 folder 里面去。 compilation.assist 是一个目录名,遍历出文件名之后使用 source 拿到 source 在放回 folder 中。

第二步将文件输出为 zip
代码语言:txt
AI代码解释
复制
zip.generateAsync({
  type:'nodebuffer'
  	}).then((content)=>{
  	const outputPath = path.join(Compilation.options.output.path 	,this.options.filename + '.zip');
  const outputRelativePath = path.relative(
    Compilation.options.output.path,
    outputPath
  )
  Compilation.assets[outputRelativePath] = new RawSource(content);
    callback()
    console.log(Compilation.options)
  })
})

使用 zip.generateAsync 返回的 content 是一个 buffer ,需要使用 RawSource 将这个 buffer 转换挂到 compilation 的 assist 中 ,最后执行 callback 。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
编写一个 Webpack 的 loader
使用Webpack往往离不开loader的安装配置,手写一个loader其实非常简单,类似手写一个功能函数,下面我们来实现一个替换字符串的loader
EchoROne
2022/08/15
2830
6-1~2 如何编写一个 loader
loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。
love丁酥酥
2020/06/02
5960
6-1~2 如何编写一个 loader
Webpack编写自己的Loader和Plugin
Loader就像是一个翻译员,能把源文件经过转化后输出新的结果,并且一个文件还可以链式的经过多个翻译员翻译。 以处理SCSS文件为例:
javascript.shop
2019/09/04
7280
手把手教你撸一个 Webpack Loader
文:小 boy(沪江网校Web前端工程师) 本文原创,转载请注明作者及出处 经常逛 webpack 官网的同学应该会很眼熟上面的图。正如它宣传的一样,webpack 能把左侧各种类型的文件(webp
iKcamp
2018/03/30
1.1K0
手把手教你撸一个 Webpack Loader
详解Webpack的loader和plugin编写
loader是文件加载器,能够加载资源文件,并对这些文件进行一些处理,诸如编译、压缩等,最终一起打包到指定的文件中。
张张
2019/12/24
2.1K0
webpack实战,手写loader和plugin
对于 webpack 来说, loader 和 plugin 可以算是需求程度最为广泛的配置项了。但是呢,单单止步于配置可能还不够。如果我们自己有时候想要 diy 一个需求,但是 webpack 又没有相关的 loader 和 plugin 。那这个时候我们可能就得开始造点轮子来供给自己使用了。
gogo2027
2022/09/28
4630
重学webpack4之plugin开发
事件钩子会有不同的类型 SyncBailHook,AsyncSeriesHook,SyncHook等
疯狂的技术宅
2020/11/26
5700
Webpack揭秘——走向高阶前端的必经之路
随着前端工程化的不断发展,构建工具也在不断完善。作为大前端时代的新宠,webpack渐渐成为新时代前端工程师不可或缺的构建工具,随着webpack4的不断迭代,我们享受着构建效率不断提升带来的快感,配置不断减少的舒适,也一直为重写的构建事件钩子机制煞费苦心,为插件各种不兼容心灰意冷,虽然过程痛苦,但结果总是美好的。经历了一番繁琐的配置后,我常常会想,这样一个精巧的工具,在构建过程中做了什么?我也是抱着这样的好奇,潜心去翻阅相关书籍和官方文档,终于对其中原理有所了解,那么现在,就让我们一起来逐步揭开webpack这个黑盒的神秘面纱,探寻其中的运行机制吧。
IMWeb前端团队
2019/12/03
4960
Webpack揭秘——走向高阶前端的必经之路
如何实现一个Webpack Loader
Loader 是支持链式执行的,如处理 sass 文件的 loader,可以由 sass-loader、css-loader、style-loader 组成,由 compiler 对其由右向左执行,第一个 Loader 将会拿到需处理的原内容,上一个 Loader 处理后的结果回传给下一个接着处理,最后的 Loader 将处理后的结果以 String 或 Buffer 的形式返回给 compiler。 这种链式的处理方式倒是和 gulp 有点儿类似,固然也是希望每个 loader 只做该做的事,纯粹的事,而不希望一箩筐的功能都集成到一个 Loader 中。
前端迷
2019/06/05
1.1K0
Webpack揭秘——走向高阶前端的必经之路
随着前端工程化的不断发展,构建工具也在不断完善。作为大前端时代的新宠,webpack渐渐成为新时代前端工程师不可或缺的构建工具,随着webpack4的不断迭代,我们享受着构建效率不断提升带来的快感,配置不断减少的舒适,也一直为重写的构建事件钩子机制煞费苦心,为插件各种不兼容心灰意冷,虽然过程痛苦,但结果总是美好的。经历了一番繁琐的配置后,我常常会想,这样一个精巧的工具,在构建过程中做了什么?我也是抱着这样的好奇,潜心去翻阅相关书籍和官方文档,终于对其中原理有所了解,那么现在,就让我们一起来逐步揭开webpack这个黑盒的神秘面纱,探寻其中的运行机制吧。
疯狂的技术宅
2019/03/27
6110
Webpack揭秘——走向高阶前端的必经之路
webpack原理概述
Entry: 入口,Webpack 执行构建的第一步将从 Entry 开始,可抽象成输入。
用户2303251
2018/06/07
1.4K0
Webpack 原理系列七:如何编写loader
关于 Webpack Loader,网上已经有很多很多的资料,很难讲出花来,但是要写 Webpack 的系列博文又没办法绕开这一点,所以我阅读了超过 20 个开源项目,尽量全面地总结了一些编写 Loader 时需要了解的知识和技巧。包含:
Tecvan
2021/12/09
1.1K0
Webpack 原理系列七:如何编写loader
Webpack打包流程分析
webpack 在前端工程领域起到了中流砥柱的作用,理解它的内部实现机制会对你的工程建设提供很大的帮助(不论是定制功能还是优化打包)。
gogo2027
2022/12/07
9320
webpack 基础知识整理
webpack是一个 模块打包工具,支持所有的打包语法,比如 ES Module、CommonJS、CMD、AMD。初期的webpack是用来模块打包js的,发展到现在,已经可以打包很多种文件类型,比如 css、img 。
神葳
2021/01/22
1.4K0
Webpack - 手把手教你写一个 loader / plugin
也就是说,webpack 把任何文件都看做模块,loader 能 import 任何类型的模块,但是 webpack 原生不支持譬如 css 文件等的解析,这时候就需要用到我们的 loader 机制了。 我们的 loader 主要通过两个属性来让我们的 webpack 进行联动识别:
用户4456933
2021/07/12
5560
Webpack - 手把手教你写一个 loader / plugin
前端Webpack Loader API 知多少
上篇文章简单介绍了一下如何简单实现一个loader,今天聊一聊Loader API相关的内容。
terrence386
2022/07/14
4940
前端Webpack Loader API 知多少
教你手写webpack常用loader
webpack 作为目前主流的前端构建工具,我们几乎每天都需要与它打交道。个人认为一个好的开源产品壮大的原因应该包括核心开发者的稳定输出以及对应生态的繁荣。对于生态来说, webpack 是一个足够开放的类库,提供了可插拔的方式去自定义一些配置,包括配置 loader 和 plugin ,本篇我们主要讨论loader。
gogo2027
2022/10/18
3700
深入理解webpack
1 初始化:启动构建,读取与合并配置参数,加载 Plugin,实例化 Compiler。
用户6835371
2021/06/01
1K0
多图详解,一次性搞懂Webpack Loader
Webpack 是一个模块化打包工具,它被广泛地应用在前端领域的大多数项目中。利用 Webpack 我们不仅可以打包 JS 文件,还可以打包图片、CSS、字体等其他类型的资源文件。而支持打包非 JS 文件的特性是基于 Loader 机制来实现的。因此要学好 Webpack,我们就需要掌握 Loader 机制。本文阿宝哥将带大家一起深入学习 Webpack 的 Loader 机制,阅读完本文你将了解以下内容:
童欧巴
2021/08/20
1.1K0
多图详解,一次性搞懂Webpack Loader
Webpack 深入浅出之公司级分享总结(内附完整ppt)
前段时间,在公司做了个 Webpack 的分享。听众40多人,感觉还不错。所以总结一下,先看一下ppt的目录:
winty
2019/12/21
2.5K4
相关推荐
编写一个 Webpack 的 loader
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档