深度解读Webpack中的loader原理

news2025/7/12 23:53:17

一、前言

webpack 是一个现代 JavaScript 应用的静态模块打包器。那么 webpack 是怎样实现不同种类资源模块加载的呢?

没错就是通过 loader。loader 用于对模块的源代码进行转换。loader 可以使你在 import 或加载模块时预处理文件。

我们带着下面几个问题,彻底吃透 loader ~

二、为什么要使用 loader

webpack 是如何加载资源模块的呢?我们先试着用 webpack 直接打包项目中的 css 文件。

初始化一个 webpack 项目,目录如下:

在 src 目录下新建了一个 index.css 文件,这里新建这个文件的目的就是以 css 文件为入口,尝试使用 webpack 单独打包它。

/* index.css */
body {margin: 0 auto;padding: 0 20px;width: 1000px;background-color: #ccc;
} 

调整下 webpack 配置,让入口文件路径指定为 index.css 的路径。

// webpack.config.js
module.exports = {entry: "./src/index.css",output: {filename: "bundle.js",},
}; 

然后我们到终端运行 npx webpack 命令,你会发现命令行会提示 Module parse failed: Unexpected token (1:5) 模块解析错误。

细心的同学会发现后面还紧跟着一句解决方案:You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file.

大致的意思就是说,您可能需要适当的 loader 来处理此文件类型,目前没有配置 loader 来处理此文件。

这里,loader 的重要性就凸显出来了。

三、怎么配置 loader

还是接着刚才打包 index.css 报错的问题。想加载 css 文件,我们可以试试常用的 css-loader。

yarn add css-loader -D 

webpack 配置也同步改下:

// webpack.config.js
module.exports = {mode: "none", // 避免不指定打包模式时弹出警告...module: {rules: [{test: /\.css$/,use: "css-loader",},],},
}; 

webpack 配置中 module 属性添加 rules 对象数组。这里面的 test 属性值为一个正则表达式,匹配当前 loader 对应处理文件的格式。use 属性值为 loader 名称。

再打包就不会报错了。

如果想要 index.css 模块在页面中生效,只需要额外添加一个 style-loader,样式就 OK 了。

style-loader 的作用可以理解为:建立了一个 style 标签,这个标签里面带入了 css 样式。标签最后追加到页面上。##### 参考webpack视频讲解:进入学习

注意配置多个 loader 时,执行顺序是从后往前执行的:

  • 最后的 loader 最早调用,将会传入原始资源内容
  • 第一个 loader 最后调用,期望值是传出 JavaScript 和 source map(可选)
  • 中间的 loader 执行时,会传入前一个 loader 传出的结果

所以 css-loader 放在最后。具体配置如下:

// webpack.config.js
module.exports = {...module: {rules: [{test: /\.css$/,use: ["style-loader", "css-loader"],},],},
}; 

假如你还要用到 less-loader,同理可知 rules 中 use 属性值应该为:["style-loader", "css-loader", "less-loader"]

四、怎么写一个 loader

想要实现的大致流程:

接下来,我们尝试实现上图 css-loader 和 style-loader 的简单版本。

4.1 创建 loader

我们在项目根目录下创建好 css-loader.js 和 style-loader.js 文件。

主要代码如下:

├── src ····································· source dir│ ├── index.css ······················· css module
+ │ └── index.js ························ entry module
+ ├── css-loader.js ······················· css loader├── package.json ························ package file
+ ├── style-loader.js ····················· style loader└── webpack.config.js ··················· webpack config file 
/* index.css */
body {margin: 0 auto;padding: 0 20px;width: 1000px;background-color: #ccc;
} 
// index.js
import "./index.css";
console.log("loader ok!"); 

每个 webpack 的 loader 都需要导出一个函数,这个函数就是我们这个 loader 对资源的处理过程,它的输入就是加载到的资源文件内容,输出就是我们加工后的结果。我们通过 source 参数接收输入,通过返回值输出。这里我们先尝试打印一下 source,然后在函数的内部直接返回一个字符串 hello webpack css-loader!,具体代码如下所示:

// css-loader.js
module.exports = (source) => {console.log(source);return "hello webpack css-loader!";
}; 

我们回到 webpack 配置文件中调整一下加载器规则,调整之后使用的加载器就是我们刚刚编写的这个 css-loader.js 模块,具体代码如下:

// webpack.config.js
module.exports = {mode: "none",// 入口改为 index.jsentry: "./src/index.js",output: {filename: "bundle.js",},module: {rules: [{test: /\.css$/,// 改下这里use: ["./css-loader"],},],},
}; 

温馨提示:这里的 use 中不仅可以使用模块名称,还可以使用模块文件路径,这点与 Node 中的 require 函数相同。

配置完成后,我们再次打开命令行终端运行打包命令,如下图所示:

从报错信息可以看出,loader 函数的参数就是文件的内容。

错误提示说: You may need an additional loader to handle the result of these loaders. (我们可能还需要一个额外的加载器来处理当前加载器的结果)

温馨提示:其实 webpack 加载资源文件的过程最后的结果必须是一段标准的 JS 代码字符串。

正常流程:

我们现在应该想到是 css-loader 出了问题。

4.2 css-loader

css-loader 主要作用就是将多个 css 模块整合到一起。

module.exports = (source) => {// 匹配 url(xxx) 类似结构const reg = /url((.+?))/g;// 位置下标let pos = 0;// 当前匹配的代码片段let current;const arr = ["let list = []"];while ((current = reg.exec(source))) {const [matchUrl, g] = current;const lastPos = reg.lastIndex - matchUrl.length;arr.push(`list.push(${JSON.stringify(source.slice(pos, lastPos))})`);pos = reg.lastIndex;arr.push(`list.push('url(' + require('${g}') + ')')`);}arr.push(`list.push(${JSON.stringify(source.slice(pos))})`);arr.push(`module.exports = list.join('')`);// 每行代码之间增加一个回车return arr.join("\n");
}; 

大致思路:

  • 整个 css 代码片段以 url(xxx) 类似结构为节点分成多个部分
  • url 里的路径改为 require 引入
  • 用数组的形式将 css 代码拼凑起来最后形成一个整体

loader 打包结果如下图:

这是输出的 bundle.js 的片段,就是把我们刚刚返回的字符串直接拼接到了该模块中。这里也解释了刚才打包语法报错的问题(loader 处理完必须返回 JS 代码)。

4.3 style-loader

style-loader 会把整合的 css 部分挂载到 head 标签中。

代码如下:

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

4.4 写 loader 之后的总结

loader 就是一个函数,一旦有模块被 import 或者 require 时它就会去拦截这些模块的源码,对其进行改造,然后输出到另一个模块中,循环往复,最终输出到入口文件中,形成最终的代码。

也正是有了 loader 机制,我们才能通过 webpack 去加载任何我们想要加载的资源。

最后

最近还整理一份JavaScript与ES的笔记,一共25个重要的知识点,对每个知识点都进行了讲解和分析。能帮你快速掌握JavaScript与ES的相关知识,提升工作效率。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/347724.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

css中的盒子模型

1、盒子模型:CSS中规定盒子分别由:内容区域(content)、内边距区域(padding)、边框区域(border)、外边距区域(margin)构成。 2、盒模型分为IE盒模型和W3C标准…

内存泄漏检测组件 -- hook

目录 hook malloc与free出现的问题 builtin_return_address(N) C/CLinux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂 hook malloc与free出现的问题 #define _GNU_SOURCE #include <stdio.h> #include <dlfcn.h> #include <stdlib.h> /****…

Android Studio多渠道打包及自动化构建

Android 有不同的应用市场&#xff0c;也就是不同的渠道&#xff0c;需要为每个应用市场打一个安装包&#xff0c;但主要的代码是一样的&#xff0c;可能部分资源不一样&#xff0c;部分代码不一样&#xff0c;如果每个渠道都需要修改&#xff0c;然后打包&#xff0c;非常耗时…

思科三层交换机配置

三层交换机是什么为什么会有三层交换机三层交换机的工作原理三层交换机的应用1.三层交换机是什么 三层交换机就是具有部分路由器功能的交换机&#xff0c;工作在OSI网络标准模型的第三层&#xff1a;网络层。三层交换机的最重要目的是加快大型局域网内部的数据交换&#xff0c;…

操作系统-处理机调度

1.处理机调度的概念、层次1.1调度的基本概念制定某种规则来决定处理任务的顺序。1.2调度的三个层次高级调度&#xff08;作业调度&#xff09;中级调度&#xff08;内存调度&#xff09;进程的挂起态与七状态模型低级调度&#xff08;进程调度&#xff09;小结2.进程调度的时机…

VMware安装Linux虚拟机后忘记root密码处理方法

OS版本&#xff1a;Red Hat 7.7 问题说明&#xff1a; 之前用VMWare安装了一台Linux虚机&#xff0c;由于长期没使用&#xff0c;导致忘记了root密码。所以需要修改root密码。 Root密码修改 现将修改root密码的操作步骤记录如下。 1.启动虚拟机&#xff0c;出现启动倒计时…

2023年,智能家居老板如何低成本多开10家店?

作者 | 启明 编辑 | 小沐 出品 | 智哪儿 zhinaer.cn2023年智能家居老板需要一些什么样的新思维来助力业绩增长呢&#xff1f;智哪儿从今天起推出系列观察文章与各位老板共勉。在《三体》电视剧中&#xff0c;三体人在乱纪元通过“脱水”来延续生命&#xff0c;在恒纪元则通过“…

mac 安装、配置、卸载mysql

文章目录mac 安装、配置、卸载mysql安装下载mysql dmg包安装mysql服务启动mysql服务配置系统环境变量配置修改默认密码配置运行远程连接配置my.cnf文件卸载mac 安装、配置、卸载mysql 安装 下载mysql dmg包 下载地址&#xff1a;https://downloads.mysql.com/archives/commu…

面试必问:进程和线程的区别(从操作系统层次理解)

1.什么是进程&#xff1f;为什么要有进程&#xff1f; 进程有一个相当精简的解释&#xff1a;进程是对操作系统上正在运行程序的一个抽象。 这个概念确实挺抽象&#xff0c;仔细想想却也挺精准。 我们平常使用计算机&#xff0c;都会在同一时间做许多事&#xff0c;比如边看…

springboot(6)之前端传递参数的方式 普通 集合 数组

实体类传递 首先我们在后端定义一个实体类&#xff0c;通过lombok插件重写 有参 无参 get set toString 方法, 然后前端发送数据&#xff0c;后端就会自动收到&#xff0c;然后属性填写 后端代码如下 AllArgsConstructor Data NoArgsConstructor public class role …

【Pygame实战】有趣又益智的游戏:看图猜四字成语,最后一个几乎没人会,哪位学霸来猜猜?这么难的嘛?(猜个锤子)

导语 看图猜成语&#xff0c;是考验一个人的反应能力&#xff0c;也考验一个人的右脑思维。 据说越聪明的人&#xff0c;这道题的完成率越高。你想试一试嘛&#xff1f;今天就给你这次机会啦&#xff01; 所有文章完整的素材源码都在&#x1f447;&#x1f447; 粉丝白嫖源码…

RocketMQ5.0.0消息存储<四>_刷盘机制

目录 一、刷盘概览 二、Broker刷盘机制 1. 同步刷盘 2. 异步刷盘 1)&#xff1a;未开启堆外内存池 2)&#xff1a;开启堆外内存池 三、参考资料 一、刷盘概览 RocketMQ存储与读写是基于JDK NIO的内存映射机制&#xff08;MappedByteBuffer&#xff09;&#xff0c;消息存储…

深度解析 JavaScript 严格模式:利弊长远的考量

前言 ECMAScript 5首次引入严格模式的概念。严格模式用于选择以更严格的条件检查JavaScript代码错误&#xff0c;可以应用到全局&#xff0c;也可以应用到函数内部。 严格模式的好处是可以提早发现错误&#xff0c;因此可以捕获某些 ECMAScript 问题导致的编程错误。 理解严格…

前端vue实现系统拦截跳转外链并进入跳转询问界面

跳转询问界面如下图所示&#xff1a; 给自己挖坑的实现方式&#xff0c;最终解决方案请看最底下 思路&#xff1a;正常情况下我们有2种方式跳转外链 第一种非a标签&#xff0c;我们手动添加事件进行跳转 <div class"dingdan public-padding p-item" click&quo…

Python 条件语句是什么?

Python条件语句是通过一条或多条语句的执行结果&#xff08;True或者False&#xff09;来决定执行的代码块。 可以通过下图来简单了解条件语句的执行过程: Python程序语言指定任何非0和非空&#xff08;null&#xff09;值为true&#xff0c;0 或者 null为false。 Python 编程…

家政服务小程序实战教程12-详情页

我们的家政服务小程序已经完成了首页和分类展示页面的开发&#xff0c;接下来就需要开发详情页了。在详情页里我们展示我们的各项服务内容&#xff0c;让用户可以了解每项家政服务可以提供的内容。 低码开发不像传统开发&#xff0c;如果开发详情页需要考虑每个字段的类型&…

【TPC证书报错--箱码校验失败】

证书管理—>交易证书管理—>编辑&#xff0c;然后就报错了。 1.这个报错&#xff0c;一般是指一个箱码&#xff0c;【产出/报工】接口失败了&#xff0c;但是【成品入库】和【成品出口】成功了。 2.大概就是【成品出库】接口&#xff0c;会传【销售单号】和【箱码】2个…

ArcGIS与地理加权回归【三】

开 工 大 急 原址链接&#xff1a; ArcGIS与地理加权回归【三】https://mp.weixin.qq.com/s/x85EXKImSHio1IZovW9qdA 接着5个月之前.......ArcGIS与地理加权回归GWR【二】以及MGWR软件下载 在ASU下载了样例“关于影响佐治亚州受教育水平”的数据。在上一篇已简单介绍…

类和对象(下)(一)

类和对象&#xff08;下&#xff09;&#xff08;一&#xff09;1.再谈构造函数1.1构造函数体赋值1.2初始化列表1.3explicit关键字2.static成员2.1概念2.2特性3.匿名对象&#x1f31f;&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f;&#x1f31f; &#x…

Spring框架源码(五) @configuration源码深度解析

Configuration 注解是spring-context模块提供的一个给开发者使用的配置类注解&#xff0c;开发者可以通过Configuration注解来定义配置类&#xff0c;也可以使用xml形式注入。 例如配置数据库配置&#xff0c;定义一个配置类&#xff0c;注入数据源DataSource, 事务管理器Trans…