# webpack源码(二)
# 手写plugin
直接拷一下官方的例子
plugin/index.js
// A JavaScript class.
class MyExampleWebpackPlugin {
// Define `apply` as its prototype method which is supplied with compiler as its argument
apply(compiler) {
// Specify the event hook to attach to
compiler.hooks.emit.tapAsync(
'MyExampleWebpackPlugin',
(compilation, callback) => {
console.log('This is an example plugin!');
console.log(compilation);
callback();
}
);
}
}
module.exports = MyExampleWebpackPlugin;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 创建一个类,就是我们的插件名
- 里面要有一个函数叫
apply
,apply
里面一串compiler.hooks.emit.tapAsync
挺关键的 - 把插件导出,在
webpack.config.js
中引入,执行打包的时候就会看到可以打印出东西
看一下webpack的源码
找到 node_modules / webpack / lib / Compiler.js
看到 Compiler
这个核心的东西是继承自 tapable
的,包括它的一些方法
const {
Tapable,
SyncHook,
SyncBailHook,
AsyncParallelHook,
AsyncSeriesHook
} = require("tapable");
// ...
class Compiler extends Tapable {}
2
3
4
5
6
7
8
9
10
那么这个 tapable
是什么呢,我们一探究竟
创建一个demo.js 文件,安装一下 tapable
yarn add tapable --dev
const {
SyncHook
} = require('tapable');
const run = new SyncHook(['complation']);
// 订阅者
run.tap('1', function (complation) {
console.log('这是一个tap1');
})
run.tap('2', function (complation) {
console.log('这是一个tap2');
})
run.tap('3', function (complation) {
console.log('这是一个tap3');
})
run.call('webpack');
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- 先引用一下
SyncHook
- 定义一下 run
- 调用一下
run
上面的tap
,其实就是个订阅者 - 发布完订阅之后,最后 call 调用一下,运行可以看到挨个输出了
当然 tapable
中还有很多钩子函数,下面就不举例了,直接贴出来
const {
SyncHook,
SyncBailHook,
SyncWaterfallHook,
SyncLoopHook,
AsyncSeriesHook,
AsyncSeriesBailHook,
AsyncSeriesWaterfallHook,
AsyncParallelBailHook,
AsyncParallelHook
} = require("tapable");
2
3
4
5
6
7
8
9
10
11
SyncHook
同步串行 不关心返回值SyncBailHook
同步串行 如果返回值不为null 就省略之后的函数SyncWaterfallHook
同步串行 可以拿到之前函数的返回值SyncLoopHook
同步串行 返回true循环执行 直到返回undefinedAsyncParallelHook
异步 不关心返回值AsyncParallelBailHook
异步 如果返回值不为null,就省略之后的函数AsyncSeriesHook
异步 不关心callback返回值AsyncSeriesBailHook
异步 如果返回不为null,就继续执行AsyncSeriesWaterfallHook
异步 上一个异步函数的值会传到下一异步函数
这样在写webpack插件的时候我们就可以通过 compiler 去调用这些钩子函数来达到不同的目的了
# webpack 编译流程
- Complier.run() 开始构建
- 创建 Complation 挂载资源文件 -> 生产Chunk
- 使用 Parser 从Chunk 里面解析依赖,使用 Module 和 Dependency 管理模块之间的相互依赖
- 使用 Template 基于 Compilation 的数据生成代码
# webpack5
在使用 webpack4 的时候我们使用异步引入的方式引入文件,打包出来的chunk会是 0 1 2 的命名,这样无法进行持久化缓存,解决办法就通过魔法注释 /import( /* webpackChunkName:"sync" */ './sync')/
的方式去改chunk名,但是如果文件很多的话也很不好
在 webpack5 中进行了改进
首先安装一下webpack5
yarn add webpack@next --dev
也是创建几个测试用的文件,在index中异步引入两个文件
import('./async2').then((_)=>{
console.log(_)
})
import('./async').then((_) => {
console.log(_)
})
console.log('京程一灯');
2
3
4
5
6
7
通过 dev 的方式打包
直接用 路径名 + 文件名 命名
再看看 prod 方式的打包
打出来的文件是串数字,这是webpack5的新东西,叫chunkids
webpack 还有一些新特性,包括多线程、拆包等等
# webpack为什么慢
loader (string -> ast -> string) * n
使用 speed-measure-webpack-plugin
来监控打包过程中较大的部分
使用 happypack
开启多线程打包
const Happypack = require('happypack');
const os = require('os');
const happypackThreal = Happypack.ThreadPool({size: os.cpus().length});
module.exports = {
rules:[
{
test: /\.js$/,
loader: 'happypack/loader?id=babeljs'
}
],
plugins: [
new Happypack({
id: 'babeljs',
loader: ['babel-loader?cacheDirectory=true'],
threadPool: happypackThreal
})
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
使用 cache-loader
使用 thread-loader
← webpack源码(一) 手写 Koa →