一、为什么webpack默认只有js和json?
因为webpack是基于node。node是遵循CommonJS规范,最早时候的webpack是遵循CommonJS规范
二、Loader
原因:webpack默认只能处理js和json文件。很多文件无法打包,就需要Loader。所以Loader负责文件转换
Loader用于将模块的原内容按照一定规则转换成新内容
- 配置方法
module.exports = {
// ...
module: {
rules: [
{ test: /\.txt$/, use: "raw-loader" }
]
}
}
执行顺序
从右到左、从下到上
如何实现一个Loader
- Loader是一个
函数,注意不能是箭头函数,因为函数内可以通过this来访问loaderContext,从而可以获取一些相关配置(见下面代码this.query注释) - 函数接收source,作为上一个阶段处理返回的代码。处理完成后返回(
return)。或者调用this.callback函数
// source: 获取到的代码,用于后续处理
module.exports = function(source) {
/**
1.this指向loaderContext
2.如果loader配置了options对象,那么this.query指向options
*/
const options = this.qurey;
// 这里对代码进行处理
var nextSource = formatSource(source)
// 处理完成了,返回代码,交给下一个loader
// 1.this.callback(null, nextSource) 。多在异步使用
// 2. return nextSource。多在同步使用
}
三、plugin
- 配置方法
module.exports = {
// ...
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
]
}
如何实现一个Plugin
Plugin主要负责功能扩展,在webpack运行的生命周期hooks中会广播许多事件,插件plugin就通过监听这些事件,并拦截webpack的执行,可以在特定阶段执行自己的插件任务
-
插件是一个
对象。或者是一个构造函数。下面以对象为例 -
对象里必须包含
apply函数。参数compiler就是执行webpack获取到的compiler -
compiler各个生命周期的
tap函数(tap函数只是例子,有同步和异步方法,要看具体生命周期),接受两个参数,一个是事件名称,一个是回调函数,回调函数参数叫compilation -
compilation 对象包含了当前的
模块资源、编译生成资源、变化的文件等。当 Webpack 以开发模式运行时,每当检测到一个文件变化,一次新的 Compilation 将被创建。Compilation 对象也提供了很多事件回调供插件做扩展。通过 Compilation 也能读取到 Compiler 对象。 -
实现一个同步的钩子 同步的不需要有callback
class MyPlugin {
apply(compiler) {
// 找到合适的生命周期,注入事件钩子
compiler.hooks.emit.tap("MyPlugin", compilation => {
// compilation:当前打包构造流程的上下文
console.info(compilation);
// do something...
})
}
}
- 一个异步的钩子
下面代码有注释,异步的必须调用callback函数,因为是
拦截webpack编译, 只有callback(),才能继续往下执行构建
const pluginName = 'ConsoleLogOnBuildWebpackPlugin';
class ConsoleLogOnBuildWebpackPlugin {
apply(compiler) {
compiler.hooks.run.tapAsync(pluginName, (compilation, callback) => {
setTimeout(() => {
console.log("webpack 构建过程开始!");
callback(); // callback 方法为了让构建继续执行下去,必须要调用
}, 1000);
});
}
}
plugin中有异步请求会阻塞后面的plugin吗?
先不要急着回答这个问题,要先去理解,什么是plugin后面的plugin,每一个plugin对应着webpack hooks不同的生命周期,所以理论上plugins数组的顺序并不能代表着执行顺序。
答案:会
如果存在异步请求,需要注册异步hooks(tapAsync或者tapPromise),这个时候接收的函数,有一个callback回调函数,只有执行callback()后,编译才会继续往下执行