之前在开发过程中一虽然了解过一些webpcak的基础知识,但是对webpack的流程还是知道的太少,所以这次学习了下webpack中loader的知识。
webpack 工作流程
关于 webpack 的工作流程,简单来说可以概括为以下几步:
1、参数解析
2、找到入口文件
3、调用 Loader 编译文件
4、遍历 AST,收集依赖
5、生成 Chunk
6、输出文件
关于 Loader
Loader 的作用很简单,就是处理任意类型的文件,并且将它们转换成一个让 webpack 可以处理的有效模块。
- Loader 的配置和使用
1.1 在 config 里配置
Loader 可以在 webpack.config.js里配置,这也是推荐的做法,定义在 module.rules 里:
// webpack.config.js
module.exports = {
module: {
rules: [
{ test: /\.js$/, use: 'babel-loader' },
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader' },
{ loader: 'postcss-loader' },
]
}
]
}
};
每一条 rule 会包含两个属性:test 和 use,比如 { test: /.js$/, use: 'babel-loader' } 意思就是:当 webpack 遇到扩展名为 js 的文件时,先用 babel-loader 处理一下,然后再打包它。
use 的类型:string|array|object|function:
string: 只有一个 Loader 时,直接声明 Loader,比如 babel-loader。
array: 声明多个 Loader 时,使用数组形式声明,比如上文声明 .css 的 Loader。
object: 只有一个 Loader 时,需要有额外的配置项时。
function: use 也支持回调函数的形式。
注意: 当 use 是通过数组形式声明 Loader 时,Loader 的执行顺序是从右到左,从下到上。比如暂且认为上方声明是这样执行的:
postcss-loader -> css-loader -> style-loader
其实就是:
styleLoader(cssLoader(postcssLoader(content)))
1.2 内联
可以在 import 等语句里指定 Loader,使用 ! 来将 Loader分开:
import style from 'style-loader!css-loader?modules!./styles.css';
内联时,通过 query 来传递参数,例如 ?key=value。
一般来说,推荐使用统一 config 的形式来配置 Loader,内联形式多出现于 Loader 内部,比如 style-loader 会在自身代码里引入 css-loader:
require("!!../../node_modules/css-loader/dist/cjs.js!./styles.css");
- Loader 类型
2.1 同步 Loader
module.exports = function(source) {
const result = someSyncOperation(source); // 同步逻辑
return result;
}
一般来说,Loader 都是同步的,通过 return 或者 this.callback 来同步地返回 source转换后的结果。
2.2 异步 Loader
在 Loader 里做一些异步的事情,比如说需要发送网络请求。如果同步地等着,网络请求就会阻塞整个构建过程,这个时候我们就需要进行异步 Loader,可以这样做:
module.exports = function(source) {
// 告诉 webpack 这次转换是异步的
const callback = this.async();
// 异步逻辑
someAsyncOperation(content, function(err, result) {
if (err) return callback(err);
// 通过 callback 来返回异步处理的结果
callback(null, result, map, meta);
});
};
2.3 Pitching Loader
{
test: /\.js$/,
use: [
{ loader: 'aa-loader' },
{ loader: 'bb-loader' },
{ loader: 'cc-loader' },
]
}
Loader 总是从右到左被调用。上面配置的 Loader,就会按照以下顺序执行:
cc-loader -> bb-loader -> aa-loader
每个 Loader 都支持一个 pitch 属性,通过 module.exports.pitch 声明。如果该 Loader 声明了 pitch,则该方法会优先于 Loader 的实际方法先执行,官方也给出了执行顺序:
|- aa-loader `pitch`
|- bb-loader `pitch`
|- cc-loader `pitch`
|- requested module is picked up as a dependency
|- cc-loader normal execution
|- bb-loader normal execution
|- aa-loader normal execution
也就是会先从左向右执行一次每个 Loader 的 pitch 方法,再按照从右向左的顺序执行其实际方法。
Loader 工作流程简述
我们来回顾一下 Loader 的一些特点:
Loader 是一个 node 模块;
Loader 可以处理任意类型的文件,转换成 webpack 可以处理的模块;
Loader 可以在 webpack.config.js 里配置,也可以在 require 语句里内联;
Loader 可以根据配置从右向左链式执行;
Loader 接受源文件内容字符串或者 Buffer;
Loader 分为多种类型:同步、异步和 pitching,他们的执行流程不一样;
webpack 为 Loader 提供了一个上下文,有一些 api 可以使用;
我们根据以上暂时知道的特点,可以对 Loader 的工作流程有个猜测,假设有一个 js-loader,它的工作流程简单来说是这样的:
- webpack.config.js 里配置了一个 js 的 Loader;
- 遇到 js 文件时,触发了 js-loader;
- js-loader 接受了一个表示该 js 文件内容的 source;
- js-loader 使用 webapck 提供的一系列 api 对 source 进行转换,得到一个 result;
- 将 result 返回或者传递给下一个 Loader,直到处理完毕。
未完待续。。。
网友评论