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

【webpack】自定义loader

📝个人主页:爱吃炫迈
💌系列专栏:前端工程化
🧑‍💻座右铭:道阻且长,行则将至💗

文章目录

  • loader
  • loader引入方式
  • loader传入/接收参数
    • 传入参数
    • 接收参数
  • loader返回值
    • return source
    • this.callback()
  • 同步/异步loader
    • 使用async和await进行处理
    • 使用loader提供的this.async进行处理
  • loader单一功能原则
  • loader实战
    • style-loader
    • css-loader
    • less-loader


loader

一个loader就是一个Node.js模块,这个模块需要导出一个函数,这个导出函数的工作就是获得处理前的源内容,对源内容进行处理后,返回处理后的内容

实现一个替换文件中姓名的loader。例如将“小张”替换成“小李”

name.js

export const name = "小张"

index.js

import { name } from "./name.js"function showInfo() {console.log("name")
}showInfo()

运行代码输出:

小张

现在我们希望将小张替换成小李:

  1. 创建replace-loader

myLoaders/replace-loader.js

module.exports = function(source) {// source为compiler传递给loader的一个文件的源内容const cotent = source.replace("小张", "小李")// 该处理函数需要返回处理后的内容return cotent
}
  1. 使用loader

webpack.config.js

const path = require("path")module.export = {mode: "production",entry: "./src/index.js",output: {path: path.resolve(__dirname, "dist"),filename: "[name].js",},module: {rules: [{test: /\.js$/,use:"./myLoaders/replace-loader.js"// 本地loader, 要填写本地的路径}]},
}

打包后我们就会发现实现我们的想要的功能啦~~~

因为我们使用的是本地的loader,因此需要填写本地的路径。loader的引入主要有以下几种方式:

loader引入方式

  1. 如果是npm包安装的loader,那么直接写loader名称即可
{test: /\.js$/,use:'babel-loader'
}
  1. 如果是本地自定义的loader,那么需要写本地loader的地址
{test: /\.js$/,use: path.resolve(__dirname, "./myLoaders/replace-loader")
}

如上面的代码,我们每使用一个自定义的loader,都必须使用 path 模块来解析自定义loader的路径问题,这就会导致代码变得难以维护。那可不可以像引用第三方的loader一样,只写loader 名呢?我们可以使用 resolveLoader 来解决这个问题。

  1. 如果是本地定义的loader,然后也想直接使用loader名称,那么可以取个别名
module.exports = {resolveLoader: {// 取个别名alias: {"replace-loader": path.resolve(__dirname,"myLoaders/replace-loader")}},module: {rules: [{test: /\.js$/,use: 'replace-loader',}]}
}
  1. 如果你不想取别名,还想直接使用loader,那么就定义一下loader的查找位置,loader会默认先从node_modules中查找。如果我们希望它也能够到本地查找,那么就定义一下查找位置。

ResolveLoader 用于配置 webpack 如何寻找 loader,默认情况下只会去 node_modules 目录下寻找,为了让 webpack 去加载自定义的 loader,我们需要修改 resolveLoader.modules

比如我们自定义的loader 放在 ./myLoaders 目录下,则需要如下配置:

module.exports = {resolveLoader: {// 去哪些目录下寻找 loader ,有先后顺序之分// 如下配置中,查找顺序是:先在 node_modules 目录下寻找,若找不到,再到 ./myLoaders 目录下寻找modules: ["node_modules", "./myLoaders"],},module: {rules: [{test: /\.js$/,use: 'replace-loader',}]}
}

loader传入/接收参数

传入参数

我们上述代码的功能是将“小张”替换成“小李”,假如我们希望将“小张”替换成“小王”,换成“小周”,那么我们难道每次都在loader中修改吗?这肯定不会,因此,我们需要支持配置参数。loader支持通过options进行配置:

{test: /\.js$/,use: ["replace-loader",{loader: "replace-loader",options: {name: "小李",},},],
}

接收参数

  1. this.query

webpack官方文档如何让编写一个loader中说明了loader只接收一个参数,这个参数是读取的文件内容(一个包含资源文件内容的字符串)。webpack会把所有的信息都放到上下文this中,我们可以通过this.queryAPI来获取webpack.config.js中配置的options对象:

module.exports = function (source) {// this.query 获取到的就是在webpack.config.js配置中配置的 options 对象// 通过 this.query API 获取在配置中配置的 name return source.replace("小张", this.query.name);
};
  1. loader-utils

虽然我们可以通过this.query来进行获取,但是webpack更加推荐使用loader-utils来进行操作,它提供了许多有用的工具,最常用的一种工具是获取传递给loader的选项。

  • 安装
npm i loader-utils -D
  • 使用
const { getOptions } = require('loader-utils')
module.exports = function(source)  {// const options = this.getOptions();let { name } = getOptions(this);const content = source.replace("小张",name);return content;
}

loader返回值

loader 的原理就是将输入的源内容进行处理后返回,loader的返回值涉及到一个还是多个返回值。有些情况下比如我们需要返回sourceMap,那么就需要多个返回值。 如果需要返回一个返回值,可以直接使用return。

return source

如果只有一个返回值,可以使用return返回,这种方式返回的是源内容转换后的内容

const { getOptions } = require('loader-utils')
module.exports = function (source) {// 处理sourcelet { name } = getOptions(this)const content = source.replace("小张", name)return content // 返回一个值
}

this.callback()

如果有多个值需要返回,需要使用loader本身提供的回调函数callback。这种方式可以返回除了处理内容之外的其他信息。

const { getOptions } = require('loader-utils')
const { SourceMap } = require('module')module.exports = function (source) {// 处理 sourcelet { name } = getOptions(this)const content = source.replace("小张", name);// 使用 this.callback 返回内容this.callback(null, content,SourceMap);
};

callback支持的参数如下:

callback({// 报错error: Error | Null,// 转换后的内容content: String | Buffer,// 转换后的内容得出的sourceMapsourceMap?: SourceMap,// astabstractSyntaxTree?: AST 
})

注意:

在Webpack中,每个loader都可以返回一个包含多个属性的对象。

  • content是该对象的一个属性,用于指定经过loader处理后生成的代码。
  • sourceMap是另一个属性,用于指定生成的代码的源映射表。

因此,content和sourceMap的区别在于,content是经过loader处理后生成的代码本身,而sourceMap则是一个包含了生成的代码和原始代码的映射关系的JSON对象。

事实上,如果只有一个返回值,我们也可以直接使用this.callback。

this.callback(null,content)

同步/异步loader

loader有同步异步之分,上面介绍的loader都是同步loader,因为它们的转换流程都是同步的,即转换完成后再返回结果。但在某些场景下转换内容需要异步才能完成,例如需要通过网络请求才能得到结果,如果使用同步的方式,网络请求就会阻塞整个构建过程,导致构建变得十分缓慢。

使用async和await进行处理

module.exports = async function(source)  {let {name,age} = getOptions(this);// 这里其实不是异步的,只是作为示例,可以这样处理const content =  await source.replace("小张",name);return content;
}

使用loader提供的this.async进行处理

当转换内容需要异步才能完成时,我们可以使用webpack为loader提供的this.async将这个loader变成是一个异步loader:

const { getOptions } = require('loader-utils')
const { SourceMap } = require('module')module.exports = function (source) {let { name } = getOptions(this);// 使用 setTimeout 模拟异步过程setTimeout(() => {const content = source.replace("小张", name);// 通过 callback 返回执行异步后的结果this.async(null, content,SourceMap);}, 3000);
};

loader单一功能原则

在webpack官网的如何编写一个loader中提到,webpack的loader编写最寻单一功能原则,也就是loader只能实现一个功能。比如less-loader用来处理less文件,css-loader用来处理css文件,style-loader用来将样式插入到style标签中,这些功能虽然可以放到一个loader中实现,但是为了确保loader的功能纯粹,能够让不同的loader各司其职,同时进行功能组合,最好每个loader只负责一个功能。

loader实战

style-loader

style-loader做的事情其实很简单,就是把序列化后的css内容放到style标签中,然后将style标签插入到HTML页面的head标签中

module.exports = function(source) {return `const styleTag = document.createElement('style');styleTag.innerHTML = ${source};document.head.appendChild(styleTag);`
}

css-loader

css-loader 做的事情也十分的简单,将 less-loader 转换后的 css 内容进行序列化

module.exports = function(source) {return JSON.stringify(source);
}

less-loader

less-loader 做的事情就是使用 less 模块,将 less 转换成 css

// 使用 less 模块处理 less 语法
const less = require('less');
module.exports = function(source) {less.render(source, (error, output) => {let { css } = output;this.callback(error, css)})
}

参考文章:
由浅及深实现一个自定义loader

相关文章:

【webpack】自定义loader

📝个人主页:爱吃炫迈 💌系列专栏:前端工程化 🧑‍💻座右铭:道阻且长,行则将至💗 文章目录 loaderloader引入方式loader传入/接收参数传入参数接收参数 loader返回值retur…...

【kubernetes】在k8s集群环境上,部署kubesphere

部署kubesphere 学习于尚硅谷kubesphere课程 前置环境配置-部署默认存储类型 这里使用nfs #所有节点安装 yum install -y nfs-utils# 在master节点执行以下命令 echo "/nfs/data/ *(insecure,rw,sync,no_root_squash)" > /etc/exports # 执行以下命令&#xff…...

STM32 F103C8T6学习笔记4:时钟树、滴答计时器、定时器定时中断

今日理解一下STM32F103 C8T6的时钟与时钟系统、滴答计时器、定时器计时中断的配置,文章提供原理,代码,测试工程下载。 目录 时钟树与时钟系统: 滴答计时器: 定时器计时中断: 测试结果: 测…...

代理模式【Proxy Pattern】

什么是代理模式呢?我很忙,忙的没空理你,那你要找我呢就先找我的代理人吧,那代理人总要知道 被代理人能做哪些事情不能做哪些事情吧,那就是两个人具备同一个接口,代理人虽然不能干活,但是被 代…...

Oracle切割字符串的方法,SQL语句完成。

Oracle用正则的方式循环切割字符串 需求:有一个这样子的 Str “‘CNJ-520-180500000001|CNJ-520-181200000001|CNJ-520-190300000001|CNJ-520-190100000001|CNJ-520-181200000002’” ,然后我需要拿到每一个单号,每一个单号都要走一遍固定的…...

Https、CA证书、数字签名

Https Http协议 Http协议是目前应用比较多应用层协议,浏览器对于Http协议已经实现。Http协议基本的构成部分有 请求行 : 请求报文的第一行请求头 : 从第二行开始为请求头内容的开始部分。每一个请求头都是由K-V键值对组成。请求体&#xf…...

Jmeter-压测时接口按照顺序执行-临界部分控制器

文章目录 临界部分控制器存在问题 临界部分控制器 在进行压力测试时,需要按照顺序进行压测,比如按照接口1、接口2、接口3、接口4 进行执行 查询结果是很混乱的,如果请求次数少,可能会按照顺序执行,但是随着次数增加&a…...

linux 文件权限识别及其修改

一、文件权限认识 在 Linux 系统中,一切皆文件,目录也是一种文件形式叫目录文件,它们的属性主要包含:索引节点(inode),类型、权限属性、链接数、所归属的用户和用户组、最近修改时间等内容。 如下为根目录下目录&…...

Java:简单算法:冒泡排序、选择排序、二分查找

冒泡排序 // 1、准备一个数组 int[] arr {5&#xff0c;2&#xff0c;3&#xff0c;1};//2、定义一个循环控制排几轮 for (int i 0; i < arr.length - 1; i) { // i 0 1 2 【5&#xff0c;2&#xff0c;3&#xff0c;1】 次数 // i 0 第一轮 0 1 2 …...

C、C++项目中 configure、makefile.am、makefile.in、makefile 之间的关系

一、configure、makefile.am、makefile.in、makefile 之间的关系 这四个文件都是与 GNU Make&#xff08;一个用于管理程序的编译和安装过程的工具&#xff09;有关的文件&#xff0c;它们的关系如下&#xff1a; configure&#xff1a;是一个脚本文件&#xff0c;用于根据系统…...

【网络】传输层——UDP | TCP(协议格式确认应答超时重传连接管理)

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《网络》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 现在是传输层&#xff0c;在应用层中的报文(报头 有效载荷)就不能被叫做报文了&#xff0c;而是叫做数…...

198.打家劫舍 ● 213.打家劫舍II ● 337.打家劫舍III

198.打家劫舍 class Solution { public:int rob(vector<int>& nums) {if(nums.size()0)return 0;if(nums.size()1)return nums[0];vector<int>dp(nums.size());dp[0]nums[0];dp[1]max(nums[0],nums[1]);for(int i2;i<nums.size();i)dp[i]max(dp[i-1],dp[i-…...

ArcGIS Maps SDK for JavaScript系列之一:在Vue3中加载ArcGIS地图

目录 ArcGIS Maps SDK for JavaScript简介ArcGIS Maps SDK for JavaScript 4.x 的主要特点和功能AMD modules 和 ES modules两种方式比较Vue3中使用ArcGIS Maps SDK for JavaScript的步骤创建 Vue 3 项目安装 ArcGIS Maps SDK for JavaScript创建地图组件 ArcGIS Maps SDK for …...

服务器扩展未生效

服务器扩容未生效 在阿里云付费扩容后&#xff0c;在服务器里面看未生效。 阿里云->实例与镜像->实例->选择实例->云盘->扩容进入linux服务器查看&#xff1a; df -h vda1扩容未生效。原40g->扩容后100g 解决方法&#xff1a; 1、安装growpart yum inst…...

Jenkins构建自由风格项目发布jar到服务器

前面的文章有介绍 docker安装jenkins 和 dockerjenkins发布spring项目&#xff1b;这里就不做过多的介绍&#xff0c;直接说明构建步骤。 1、选择构建一个自由风格的项目 2、 选择丢弃旧的构建 3、配置Git信息 4、构建触发器 和 构建环境可以直接跳过 5、直接来到Build Step…...

Rabbitmq延迟消息

目录 一、延迟消息1.基于死信实现延迟消息1.1 消息的TTL&#xff08;Time To Live&#xff09;1.2 死信交换机 Dead Letter Exchanges1.3 代码实现 2.基于延迟插件实现延迟消息2.1 插件安装2.2 代码实现 3.基于延迟插件封装消息 一、延迟消息 延迟消息有两种实现方案&#xff…...

miniExcel 生成excel

一、nuget dotnet add package MiniExcel --version 1.31.2 二、新建表及数据 ExampleProducts 三、这里我用了Dapper.Query方法 读取excel public virtual async Task<IActionResult> Anonymous(){try{//using (var connection _dbContext.GetDbConnection())//{//…...

Handler详解

跟Handler有关系的&#xff0c;包括Thread&#xff0c;Looper&#xff0c;Handler&#xff0c;MessageQueue Looper: 由于Looper是android包加入的类&#xff0c;而Thread是java包的类&#xff0c;所以&#xff0c;想要为Thread创建一个Looper&#xff0c;需要在线程内部调用…...

Feign忽略Https的SSL最佳方案(且保证负载均衡将失效)

同时解决Https的SSL证书验证问题和feign不支持Patch请求方法的问题 代码 1. 工具类 OkHttpUtils.java import javax.net.ssl.*; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import j…...

Neo4j之SET基础

在 Neo4j 中&#xff0c;SET 语句用于更新节点或关系的属性。它允许你修改节点或关系的属性值&#xff0c;可以单独使用&#xff0c;也可以与其他查询语句&#xff08;如 MATCH、CREATE、MERGE 等&#xff09;一起使用。以下是一些使用 SET 语句的常见例子&#xff0c;以及它们…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望

文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例&#xff1a;使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例&#xff1a;使用OpenAI GPT-3进…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

如何理解 IP 数据报中的 TTL?

目录 前言理解 前言 面试灵魂一问&#xff1a;说说对 IP 数据报中 TTL 的理解&#xff1f;我们都知道&#xff0c;IP 数据报由首部和数据两部分组成&#xff0c;首部又分为两部分&#xff1a;固定部分和可变部分&#xff0c;共占 20 字节&#xff0c;而即将讨论的 TTL 就位于首…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行

项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战&#xff0c;克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...

MySQL 知识小结(一)

一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库&#xff0c;分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷&#xff0c;但是文件存放起来数据比较冗余&#xff0c;用二进制能够更好管理咱们M…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

多模态图像修复系统:基于深度学习的图片修复实现

多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...