loader

loader就是webpack的代码转换器,因为webpack最开始只支持js文件的编译,所以我们在打包项目时,其他格式的文件需要通过loader来转换。
loade本质是一个函数,接受初始资源为参数,返回转化后的内容,所以函数内部一般会结合AST和字符串拼接对代码进行转换和加工,只能在编译阶段调用。

plugin

plugin就是webpack的插件,可以扩展webpack的功能,能在任何阶段调用。

  1. // myPlugin.js
  2. class MyPlugin {
  3. apply(compiler) {
  4. compiler.hooks.run.tap("myPlugin", compiler=> {
  5. console.log("我写的插件正在运行!!!");
  6. });
  7. }
  8. }
  9. module.exports = { MyPlugin };
  10. //webpack.config.js
  11. const { MyPlugin } = require("./plugin/MyPlugin");
  12. module.exports = {
  13. entry: {
  14. app: "./src/index.js"
  15. },
  16. plugins: [new MyPlugin()]
  17. };

插件中的apply方法可以与webpack内部运行接轨,插件从apply这个函数中拿到webpack的compiler对象,他是webpack编译器实例(后面介绍),包含整个编译周期的内容,通过compiler.hooks来访问各个周期钩子(比如run),通过tap方法将插件中自定义的回调函数,注册到该钩子上。
除了run这个钩子,还有compilation。是webpack编译之后触发的钩子:

  1. const { ConcatSource } = require("webpack-sources");
  2. class MyPlugin {
  3. apply(compiler) {
  4. //使用compilation hook 编译(compilation)创建之后,执行插件。
  5. compiler.hooks.compilation.tap("BannerPlugin", compilation => {
  6. //优化所有 chunk 资源(asset)。资源(asset)会被存储在 compilation.assets。
  7. // 每个 Chunk 都有一个 files 属性,指向这个 chunk 创建的所有文件。
  8. //附加资源(asset)被存储在 compilation.additionalChunkAssets 中。
  9. compilation.hooks.optimizeChunkAssets.tap("BannerPlugin", chunks => {
  10. for (const chunk of chunks) {
  11. for (const file of chunk.files) {
  12. compilation.updateAsset(file, old => {
  13. return new ConcatSource("/*!我在这里加入一行注释*/","\n", old);
  14. });
  15. }
  16. }
  17. });
  18. });
  19. }
  20. }
  21. module.exports = { MyPlugin };

工作流程

webpack的工作流程是串行的。分为初始化、编译、输出三个阶段。
image.png
初始化阶段:拿到配置启动编译,读取和合并参数(shell和config),加载所有plugin(外部和内置),实例化compiler,它是整个周期的指挥者,负责整个编译构建过程,包含了webpack所有的环境信息,接着注册所有的Hooks钩子。

编译阶段:从入口entry开始,对每一个模块调用对应的loader去编译内容,在找到模块依赖的模块,去递归进行编译,形成依赖图集moduleGraph(利用Parser(acorn)将入口文件转化为AST,分析导入语句,将依赖加入到dependencies中)。
如果entry是数组,说明是多入口,则会循环遍历每一个入口处理,如果是函数,说明是异步加载入口,则使用异步加载plugin处理。初始化compilation,它是单一一次编译构建的内容,每编译一次都会初始化一次。
编译好的内容存储在内存中(compilation.modules)。

输出阶段:将编译好的模块组合成chunk,然后转换成文件,这里会将AST转换回JS代码。

事件流机制

webpack声明周期中的各个hook钩子是通过Tapable实现的,类似EventCenter(观察者模式)。