webpack loader
# webpack loader
# loader 执行顺序
loader 的执行顺序是从右向左执行的
因为 webpack 选择了 compose 这样的函数式编程方式,而 gulp 却选择应用从左到右的 pipe 管道式编程。
# webpack 常用 loader
样式:style-loader(用于将 css 编译完成的样式,挂载到页面 style 标签上)、css-loader(加载 CSS)、less-loader、sass-loader 等
文件:file-loader 、url-loader、raw-loader 等
编译:babel-loader、coffee-loader 、ts-loader(将 TypeScript 转为 JavaScript)等
校验测试:eslint-loader、mocha-loader、jshint-loader 等
数据:csv-loader xml-loader(打包加载解析 csv 和 xml 文件数据)
# loader 的三种使用方式
配置方式(推荐):在 webpack.config.js 文件中指定 loader。
内联方式:在每个 import 语句中显式指定 loader。
CLI 方式:在 shell 命令中指定它们。
# loader 种类及执行顺序
inline loader(行内 loader):loader 被应用在 import/require 行内
pre loader(前置 loader):Rule.enforce 配置为
pre
normal loader(普通 loader):没有值表示是普通 loader。
post loader(后置 loader):Rule.enforce 配置为
post
1 是在我们的业务代码的模块引入语句中 2,3,4 三种 loader 是通过 webpack 配置文件中进行配置的
所有的 loader,都有两个阶段:
类似于 js 中的事件冒泡、捕获阶段(有人嫌官方的词描述的比较晦涩,修改为 loader 的标记阶段(mark stage)和执行阶段(execution/run stage))。
webpack 在使用 loader 处理资源时首先会经过 loader.pitch 阶段,pitch 阶段结束后才会读取文件而后进行 normal 阶段处理。
Pitching 阶段: loader 上的 pitch 方法,按照 后置(post)、行内(inline)、普通(normal)、前置(pre) 的顺序调用。
Normal 阶段: loader 上的 常规方法,按照 前置(pre)、普通(normal)、行内(inline)、后置(post) 的顺序调用。模块源码的转换, 发生在这个阶段。
inline loader 和 config loader 的优先级:
如果一个项目同时存在 inline loader 和 config loader,那么先解析 inline loader,再解析 config loader,可以再 inline loader 上去控制 config loader 上的配置是否执行,例如你的同事配置了 config loader,但是你不想用 config loader,只想用 inline loader 这种情形。
- 加入!前缀,将禁用所有已配置的 normal loader(普通 loader)
不使用 config loader 中的 normal loader,例如 require('!a-loader!./a.js');
- 加入!!前缀,将禁用所有已配置的 loader
不使用 config loader 中的 pre loader,normal loader,post loader,例如 require('!!a-loader!./a.js');
- 加入-!前缀,将禁用所有已配置的 preLoader 和 loader,但是不禁用 postLoaders
不使用 config loader 中的 normal loader,pre loader,例如 require('-!a-loader!./a.js');
Loader 实际的执行顺序与 loader 的类型,pitch 方法,inline-loader 的前缀都有关系。
normal loader 和 pitch loader
- 关于 normal loader 本质上就是 loader 函数本身。
// loader函数本身 我们称之为loader的normal阶段
function loader(source) {
// dosomething
return source;
}
2
3
4
5
在默认的 Loader 执行阶段这四种 loader 会按照如下顺序执行:
资源文件 => 前置(pre)loader => 普通(normal)loader => 行内(inline)loader => 后置(post)loader => webpack 编译 loader 处理后的内容
- 关于 pitch loader 就是 normal loader 上的一个 pitch 属性,它同样是一个函数:
// pitch loader是normal上的一个属性
loader.pitch = function(remainingRequest, previousRequest, data) {
// ...
};
2
3
4
pitch 阶段返回的非 undefeind 的值会造成 loader 打破原有顺序掉头执行,这就叫做熔断效果。
pitch Loader 的熔断效果:
# loader 特性
loader 支持链式调用。链中的每个 loader 会将转换应用在已处理过的资源上。一组链式的 loader 将按照相反的顺序执行。链中的第一个 loader 将其结果(也就是应用过转换后的资源)传递给下一个 loader,依此类推。最后,链中的最后一个 loader,返回 webpack 所期望的 JavaScript。
loader 可以是同步的,也可以是异步的。
# 自定义 loader
- 最简化版
module.exports = function(source) {
return source.replace(/test/, "test123");
};
2
3
- 使用 loader-utils 和 schema-utils
import { getOptions } from "loader-utils"; // 工具库,getOptions获取传递给 loader 的选项
import validateOptions from "schema-utils"; // 加载程序和插件中用于验证选项的软件包。
const schema = {
type: "object",
properties: {
test: {
type: "string",
},
},
};
export default function(source) {
const options = getOptions(this);
validateOptions(schema, options, "Example Loader");
// 对资源应用一些转换……
return `export default ${JSON.stringify(source)}`;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# loader 问题汇总
# css-loader 和 style-loader 区别
css-loader: 加载.css 文件
style-loader: 使用<style>
将 css-loader 解析后的内容挂载到 html 页面当中
# file-loader 和 url-loader 区别
相同点:file-loader 与 url-loader 都是在 webpack 中引入图片的解决方案。
不同点:
file-loader:file-loader 可以解析项目中文件的 url 引入(不仅限于 css),返回的是文件的 public URL。
url-loader:与 file-loader 不同,url-loader 可以在图片大小小于设定的 limit 的时候返回的是一个 bDataURL(base64 码),大于 limit 时会调用 file-loader 对图片进行处理。
两者关系: url-loader 封装了 file-loader,但 url-loader 不依赖于 file-loader。
# loader 和 plugin 区别
- 作用不同
loader,它是一个转换器,用于对模块源码的转换,
plugin 是一个扩展器,为了扩展 webpack 的功能,目的在于解决 loader 无法实现的其他事,从打包优化和压缩,到重新定义环境变量,功能强大到可以用来处理各种各样的任务
- 运行时机不同
loader 作用在编译模块过程中,plugins 在整个编译周期都起作用
- loader 遵循单一职责,一个 loader 只做一件事情,plugins 是基于事件机制工作,会监听 webpack 打包过程中的某些节点,作用于 webpack 打包的整个过程
参考:多角度解析 Webpack5 之 Loader 核心原理 https://juejin.cn/post/7036379350710616078 (opens new window)