当前位置: 首页 > 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中的某些数据类型是不可变的…...

华为云AI开发平台ModelArts

华为云ModelArts:重塑AI开发流程的“智能引擎”与“创新加速器”! 在人工智能浪潮席卷全球的2025年,企业拥抱AI的意愿空前高涨,但技术门槛高、流程复杂、资源投入巨大的现实,却让许多创新构想止步于实验室。数据科学家…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...

在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:

在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档&#xff0c…...

uniapp微信小程序视频实时流+pc端预览方案

方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度​WebSocket图片帧​定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐​RTMP推流​TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...

零基础设计模式——行为型模式 - 责任链模式

第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...

【Oracle】分区表

个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

C++.OpenGL (14/64)多光源(Multiple Lights)

多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

软件工程 期末复习

瀑布模型:计划 螺旋模型:风险低 原型模型: 用户反馈 喷泉模型:代码复用 高内聚 低耦合:模块内部功能紧密 模块之间依赖程度小 高内聚:指的是一个模块内部的功能应该紧密相关。换句话说,一个模块应当只实现单一的功能…...

SQL进阶之旅 Day 22:批处理与游标优化

【SQL进阶之旅 Day 22】批处理与游标优化 文章简述(300字左右) 在数据库开发中,面对大量数据的处理任务时,单条SQL语句往往无法满足性能需求。本篇文章聚焦“批处理与游标优化”,深入探讨如何通过批量操作和游标技术提…...