当前位置: 首页 > news >正文

webpack实战,手写loader和plugin

序言

对于 webpack 来说, loaderplugin 可以算是需求程度最为广泛的配置项了。但是呢,单单止步于配置可能还不够。如果我们自己有时候想要 diy 一个需求,但是 webpack 又没有相关的 loaderplugin 。那这个时候我们可能就得开始造点轮子来供给自己使用了。

因此,在今天的文章当中,将带领大家手写一个简易的 loaderplugin ,并学会如何在项目中运用自己所编写的 loaderplugin

一起来学习吧~📢

一、如何编写一个Loader

1. 碎碎念

之前的文章中我们讲到了关于 loader 的一些配置。那如果把那些引用的 loader 改为我们写的 loader ,该怎么处理呢?

现在,我们来了解一下,如何手写一个简易的 loader ,并运用到我们的项目当中。

2. 项目结构

首先用一张图,来看我们的项目结构如下图所示:

1-loader项目结构.png

其中 loaders 文件夹下放置我们想要写的 loader ,同时里面的 replaceLoader.js 文件放置我们即将要写的 loader 的代码逻辑。之后,index.js 文件是我们的入口文件,放置我们的业务逻辑。 webpack.config.js 文件放置关于 webpack 的相关配置,而 dist 文件夹内的内容,放置的是我们通过 webpack 打包后,生成的打包文件。

3. 业务代码编写

(1)入口文件代码

现在,我们先来编写入口文件 index.js 的代码。具体代码如下:

console.log('hello monday');

(2)编写loader

入口文件的内容很简单,我们想要达到的目的就是输出 hello monday 这个语句。现在,我们来编写 loader 的内容,已达到对入口文件 index.js 的内容进行修改。 replaceLoader.js 文件的代码具体如下:

module.exports = function(source) {const result = source.replace('monday', 'mondaylab');this.callback(null, result);
}

以上的代码意思为,将入口文件 index.js 文件中的 monday 替换为 mondaylab 。这样写似乎没啥问题,但是大家有没有想过,我们有时候传的属性可能会很诡异,不一定每次都能像这样以字符串的形式来替换。

所以,我们引用 webpack 官方推荐的 loadertils 这个工具,来解决这个问题。

第一步: 安装 loader-utils 插件。具体命令如下:

npm install loader-utils --save-dev

第二步: 改造 loader 文件。接下来,我们对 replaceLoader.js 文件进行改造升级,具体代码如下:

const loaderUtils = require('loader-utils');//用function的原因在于为了业务层可以调用this
//source为引入文件的源代码
module.exports = function(source) {//getOptions会自动地帮我们分析this.query,然后把参数的所有内容放在options里面去const options = loaderUtils.getOptions(this);const result = source.replace('monday', options.name);this.callback(null, result);
}

大家可以看到,通过使用 loaderUtils 插件,间接地,调用 getOptions 方法,来自动的帮我们分析 this.query ,从而取到我们想要的内容。

值得注意的是,我们还需要再了解一下 this.callback 的内容。

一般情况下,如果我们接收到了源代码 source ,那么现在我们只能对源代码做处理。但是呢,有的时候,我们想要使用一些 sourceMap ,或者对源代码分析好了之后,我们不仅想要返回源代码,还要把 sourceMap 也带回去。

因为我们 return 的时候只能 return 一个参数,其余的一些额外的内容就带不出去了。这个时候呢,我们就需要 this.callback 来帮我们把 sourceMap 给带出去。因此,一般用 this.callback 来返回内容。

(3)引用loader

现在,我们在 webpack.config.js 中,来引入我们上面的 loader具体配置如下:

const path = require('path');module.exports = {mode: 'development',entry: {main: './src/index.js'},module: {rules: [{test: /\.js/,use: [{loader: path.resolve(__dirname, './loaders/replaceLoader.js'),//上面的options.name中的nameoptions: {name: 'mondaylab'}}   ]}]},output: {path: path.resolve(__dirname, 'dist'),filename: '[name].js' }
}

通过以上方式,我们写了一个简易的 loader ,这个 loader 实现了将 monday 替换为 mondaylab 的功能。并且供我们在 webpack 中使用自己书写的 loader

(4)在loader里面做一些异步的操作

好了现在,如果我们想要给 loader 做一些异步操作,该怎么实现呢?

在我们所写的 loader 当中,加入异步操作,那么我们需要调用官方提供给我们的 this.async() 这个 API 来实现。现在,我们来改造一下 replaceLoader.js 文件的代码。具体代码如下:

const loaderUtils = require('loader-utils');module.exports = function(source) {const options = loaderUtils.getOptions(this);//调用this.async()这个API,来给异步代码使用const callback = this.async();setTimeout(() => {const result = source.replace('monday', options.name);callback(null, result);}, 1000);
}

通过这种方式,我们就可以在 loader 中编写异步代码,来达到我们想要的效果。

(5)loader路径自定义

有一个很小的注意点就是,当我们在配置 webpack.config.js 文件中, loader 的路径时,每回都要 path.resolve 去寻找路径文件。文件少的时候还好,但如果遇到多文件的时候呢?岂不是会很麻烦。

所以,我们引用 resolveLoader 来简化它。现在我们在 webpack.config.js 文件中进行改造。具体配置如下:

const path = require('path');module.exports = {// 先到node_modules中去找,找不到则去./loaders目录下去找resolveLoader: {modules: ['node_modules', './loaders']},module: {rules: [{test: /\.js/,use: [{loader: 'replaceLoader'}]}]}
}

参考 前端进阶面试题详细解答

通过配置 resolveLoader ,来对文件文件目录进行查找,从而简化了路径内容。

二、如何编写一个Plugin

1. 碎碎念

在讲解 plugin 之前,我们先来了解 loaderplugin 的区别。

当我们在源代码里面,去引入一个新的 js 文件,或者是一个其他格式的文件时,这个时候我们可以借用 loader ,来帮我们处理我们引用的 loader 文件。 loader 的作用就在于,帮助我们处理引用的模块

plugin 呢,是当我们在做打包的时候,在某些具体时刻上,比如说,当我们打包结束之后,我们要生成一个 html 文件,这个时候,我们就可以使用一个 htmlWebpackPlugin 的插件。使用它之后,他就会在打包结束之后,帮我们生成对应的 html 文件。

再比如,我们要在打包之前,把 dist 目录进行清空,这个时候我们就可以使用 cleanWebpackPlugin 来帮助我们做这件事情。

所以, plugin 插件,在什么时候生效呢?

它在我们打包过程中的某些时刻里,就是插件生效的场景

plugin 的编写相对于 loader 来说,会难一点点。但是呢,如果有看过 webpack 源码的小伙伴们可能会知道, webpack 的一些底层原理都是依据 plugin 来进行编写的。所以,我们还是有必要来学习一下 plugin 的编写。

下面就带领大家来编写一个简易的 plugin ~

2. 项目结构

对于 webpackplugin 来说,它是是基于发布者订阅的设计模式,也可以说是基于事件驱动来实现的。在这个事件驱动里,代码之间的执行,是通过事件来进行驱动的。

接下来,我们就来写一个简易的 plugin

首先用一张图,来看我们的项目结构如下图所示:

2-plugin项目结构.png

其中 plugins 文件夹下放置我们想要写的 plugin ,同时里面的 copyright-webpack-plugin.js 文件放置我们即将要写的 plugin 的代码逻辑。之后,index.js 文件是我们的入口文件,放置我们的业务逻辑。 webpack.config.js 文件放置关于 webpack 的相关配置,而 dist 文件夹内的内容,放置的是我们通过 webpack 打包后,生成的打包文件。

3. 业务代码编写

(1)入口文件代码

现在,我们先来编写入口文件 index.js 的代码。具体代码如下:

console.log('hello monday');

(2)编写plugin

现在,我们来编写 plugin 的内容, copyright-webpack-plugin.js 文件的代码具体如下:

class CopyrightWebpackPlugin {//编写一个构造器constructor(options) {console.log(options)}apply(compiler) {//遇到同步时刻compiler.hooks.compile.tap('CopyrightWebpackPlugin',() => {console.log('compiler');});//遇到异步时刻//当要把代码放到dist目录之前,要走下面这个函数//Compilation存放打包的所有内容,Compilation.assets放置生成的内容compiler.hooks.emit.tapAsync('CopyrightWebpackPlugin', (Compilation, cb) => {debugger;// 往代码中增加一个文件,copyright.txtCompilation.assets['copyright.txt'] = {source: function() {return 'copyright by monday';},size: function() {return 19;}};cb();})}
}module.exports = CopyrightWebpackPlugin;

上面的这个插件中想要实现的功能就是,获取版权信息

(3)引用plugin

现在,我们在 webpack.config.js 中,来引入我们上面的 plugin具体配置如下:

const path = require('path');
const CopyrightWebpackPlugin = require('./plugins/copyright-webpack-plugin');module.exports = {mode: 'development',entry: { main: './src/index.js'},plugins: [new CopyrightWebpackPlugin({name: 'monday'})],output: {path: path.resolve(__dirname, 'dist'),filename: '[name].js'}
}

通过上述代码,我们可以了解到,在(2)中,我们首先需要定义一个类,之后呢,在类中写一个构造器和一个 apply() 方法来调用。然后呢,大家看到(3),通过 require 的方式,来进行 new 实例 ,实例化一个插件,从而在项目中使用这个插件。

最终,我们项目进行打包时,就会生成一个 dist 目录,并且在目录下增加一个 copyright.txt 文件,并且文件中的内容就是 copyright by monday

三、结束语

在上面的文章中,讲解了关于loader和plugin的基本编写思路,以及如何在项目中对他们进行运用,相信大家对这一块内容有了基础的认识。

到这里,loader和plugin的编写讲解就结束啦!希望对大家有帮助~

相关文章:

webpack实战,手写loader和plugin

序言 对于 webpack 来说, loader 和 plugin 可以算是需求程度最为广泛的配置项了。但是呢,单单止步于配置可能还不够。如果我们自己有时候想要 diy 一个需求,但是 webpack 又没有相关的 loader 和 plugin 。那这个时候我们可能就得开始造点轮…...

STM32CubeMX按键模块化 点灯

本文代码使用 HAL 库。 文章目录前言一、按键原理图二、CubeMX 创建工程三、代码讲解:1. GPIO的输入HAL库函数:2. 消抖:3. 详细代码四,实验现象:总结前言 我们继续讲解 stm32 f103,这篇文章将详细 为大家讲…...

C#专栏目录(长期更新)

文章目录C# 基础C#进阶C#应用WPF基础WPF 3D小游戏C# 基础 1996年,微软用年薪三百万美刀的价格从Borland挖来了大神海尔斯伯格,开始了J开发,用以对抗Java。但SUN公司认为此举违反了Java开发平台的中立性,对微软提出诉讼。C#正是在…...

BurpSuite配置抓取HTTPS数据包

简介 我们在渗透测试的过程中,经常会遇到HTTPS的网站,Burp默认是没有办法抓取HTTPS的包的,想要让Burp抓取Https的包也很好办,只需要浏览器安装相关的证书即可,接下来将配置过程做一个记录。 前置条件: 1.J…...

图片转base64格式返回给前端,前端如何展示?

图片以base64形式在页面上展示出来在这里要说到Data URI scheme,它可以直接将一些小的数据直接嵌入到网页中,不需要再引入。支持格式如下data:, 文本数据data:text/plain, 文本数据data:text/html, HTML代码data:text/html;base64, base64编码的HTML代码…...

C++入门知识【超详解】

目录1.认识Chello worldC关键字2.命名空间3.std标准库4.输入输出5.缺省参数6.函数重载7.引用7.1引用的概念7.2引用的场景1.作参数2.作返回值7.3引用的注意点7.4指针和引用的区别8.auto关键字9.基于范围的for循环10.内联函数10.1概念10.2特征11. C98中的指针空值1.认识C hello …...

零基础、非计算机系学Python该如何上手?

首先我觉得要放平心态,不用过多去纠结是不是专业出身这回事。 想学那就认真去学,我们最终目标是掌握Python这门技能。 非计算机专业同时零基础,想自学Python该如何上手?分享我自学Python的几点建议吧。 1、重视基础 Python是一…...

关于 vue3 模板引用

文章目录前言1.访问模板引用2.v-for中的模板引用3.组件上的ref前言 如果我们需要直接访问组件中的底层DOM元素,可使用vue提供特殊的ref属性来访问 1.访问模板引用 在视图元素中采用ref属性来设置需要访问的DOM元素 a. 该ref属性可采用字符值的执行设置 b. 该ref属…...

Redis | 安装Redis和启动Redis服务

目录 一、Redis简介 1.1 简介 二、Redis安装 2.1 Windows安装Redis 2.2 Linux安装Redis 三、Redis服务启动和停止 3.1 Windows启动Redis服务 3.2 Linux启动Redis服务 四、Redis设置密码远程连接 4.1 为Redis登陆设置密码 4.2 设置Redis允许远程连接 五、Redis常…...

博客要考虑的最佳WordPress主题

有太多的选择会瘫痪你做决定的能力。有太多的WordPress主题,但仅仅只需要一个并且它是要合适的。我们建立了数十个 WordPress 博客并安装了数百个主题。根据我们所有的经验,我们发现Newspaper是大多数用户的最佳WordPress博客主题。这个自适应、强大的主…...

C 学习笔记 —— 函数指针

函数指针 上面的第二个char (* f) (int);写法就是函数指针的声明; 首先,什么是函数指针?假设有一个指向 int类型变量的指针,该指针储存着这个int类型变量储存在内存位置的地址。 同样,函数也有地址,因为函…...

FastDDS-3. DDS层

3. DDS层 eProsima Fast DDS公开了两个不同的API,以在不同级别与通信服务交互。主要API是数据分发服务(DDS)数据中心发布订阅(DCPS)平台独立模型(PIM)API,简称DDS DCPS PIM&#xf…...

9.2 IGMPv2

实验目的 (1) 熟悉IGMPv2的应用场景 (2) 掌握IGMPv2的配置方法 实验拓扑 实验拓扑如图9-17所示: 图9-17:IGMPv2 实验步骤 配置IP地址(请参考上一个实验)运行IGP&#xff…...

巨头混战,抢着“兜底”自动驾驶安全

诚然,中国汽车行业的发展绝对不会拘泥于电动化,必定会在电动化的基础上,迎接下半场的快速智能化。 2021年6月,长城汽车线控底盘全球首次发布。 彼时,长城汽车技术副总裁宋东先宣布,整合了线控转向、线控制…...

RightCapital 第一轮面试题

现在我们就马上开始吧! 答案在文末 JavaScript 是一门单线程的静态类型语言(单选题) 正确 错误 在 JavaScript 中下面哪种类型的值是不可变的(immutable)(单选题) Object Symbol Array Date …...

Python曲线肘部点检测-膝部点自动检测

文章目录一. 术语解释二. 拐点检测肘部法则是经常使用的法则。很多时候,可以凭人工经验去找最优拐点,但有时需要自动寻找拐点。最近解决了一下这个问题,希望对各位有用。一. 术语解释 **肘形曲线(elbow curve)**类似人胳膊状的曲线&#xff…...

【算法题】最大矩形面积,单调栈解法

力扣:84. 柱状图中最大的矩形 给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 求在该柱状图中,能够勾勒出来的矩形的最大面积。 题意很简单,翻译一下就是:求该图中…...

活动策划|深度分析年货节活动该如何策划!

四月初,不平凡的初春开始恢复往日的平静。对于新零售行业,疫情的缓解也逐渐平稳生态链的运转。2020年新零售的格局在洗礼后,业务的聚焦点也从前端促销转移到后端履约的体验闭环,同时很大程度的推进企业在危机公关下的应对。618大促…...

Idea启动遇到 Web server failed to start. Port 8080 was already in use. 报错

Idea启动遇到问题-记录 报错英文提示: APPLICATION FAILED TO START Description: Web server failed to start. Port 8080 was already in use. Action: Identify and stop the process that’s listening on port 8080 or configure this application to liste…...

Python3中zip()函数知识点总结

1.引言 在本文中,我将带领大家深入了解Python中的zip()函数,使用它可以提升大家的工作效率。 闲话少说,我们直接开始吧! 2. 基础知识 首先,我们来介绍一些基础知识点: Python中的某些数据类型是不可变的…...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来&#xff0c;尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断&#xff0c;但全球市场热度依然高涨&#xff0c;入局者持续增加。 以国内市场为例&#xff0c;天眼查专业版数据显示&#xff0c;截至5月底&#xff0c;我国现存在业、存续状态的机器人相关企…...

LeetCode - 394. 字符串解码

题目 394. 字符串解码 - 力扣&#xff08;LeetCode&#xff09; 思路 使用两个栈&#xff1a;一个存储重复次数&#xff0c;一个存储字符串 遍历输入字符串&#xff1a; 数字处理&#xff1a;遇到数字时&#xff0c;累积计算重复次数左括号处理&#xff1a;保存当前状态&a…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

JAVA后端开发——多租户

数据隔离是多租户系统中的核心概念&#xff0c;确保一个租户&#xff08;在这个系统中可能是一个公司或一个独立的客户&#xff09;的数据对其他租户是不可见的。在 RuoYi 框架&#xff08;您当前项目所使用的基础框架&#xff09;中&#xff0c;这通常是通过在数据表中增加一个…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问&#xff08;基础概念问题&#xff09; 1. 请解释Spring框架的核心容器是什么&#xff1f;它在Spring中起到什么作用&#xff1f; Spring框架的核心容器是IoC容器&#…...

快刀集(1): 一刀斩断视频片头广告

一刀流&#xff1a;用一个简单脚本&#xff0c;秒杀视频片头广告&#xff0c;还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农&#xff0c;平时写代码之余看看电影、补补片&#xff0c;是再正常不过的事。 电影嘛&#xff0c;要沉浸&#xff0c;…...

(一)单例模式

一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?

在工业自动化持续演进的今天&#xff0c;通信网络的角色正变得愈发关键。 2025年6月6日&#xff0c;为期三天的华南国际工业博览会在深圳国际会展中心&#xff08;宝安&#xff09;圆满落幕。作为国内工业通信领域的技术型企业&#xff0c;光路科技&#xff08;Fiberroad&…...