一、核心概念

  • entry:入口,webpack的构建从entry开始
  • output:输入,编译之后的文件路径、名称等相关配置
  • module:模块,在webpack中一切皆是模块,一个文件就是一个模块
  • chunk:代码块,一个代码块有多个模块组成,用于代码合并与分割
  • loader:模块转换器,用于将模块的原内容转换成新内容
  • plugin:扩展插件,用于在webpack构建见过程中,在特定时机处理相关的逻辑

一个简单的webpack配置(webpack.config.js)

  1. module.exports = {
  2. entry: {
  3. index: path.join(__dirname, 'index.js'),
  4. },
  5. output: {
  6. path: path.join(__dirname, '/dist'),
  7. filename: 'js/[name].js'
  8. },
  9. module: {
  10. rules: [
  11. { test: /\.js$/, exclude: /node_modules/, loader: "babel-loader" }
  12. ]
  13. },
  14. plugins: [
  15. new HtmlWebpackPlugin({
  16. template: 'index.html',
  17. }),
  18. ]
  19. };

二、webpack运行流程

  1. 初始化参数 (包括设置一些默认的loader和plugin)
  2. 创建compiler(只要webpack不关闭,一直存在)
  3. 在相应hook上注册插件
  4. compiler.run触发一些列钩子进行一次编译,具体后面再讲
  5. 创建compilation(一次编译过程)
  6. 。。。


三、从webpack的hooks看具体流程

在webpack中有两个重要概念compiler和compilation,compiler贯穿整个生命周期,只要webpack还是启动状态,compiler就一定存在,compilation对应一次编译过程,下面是一些主要hooks的执行顺序
初始化阶段:

合并参数 将webpack.config.js和shell中参数合并
创建compiler实例
挂载plugins
compiler.hooks.environment


compiler.hooks.afterEnvironment
挂载内置插件

new WebpackOptionsApply().process(options, compiler)


compiler.hooks.entryOption
读取配置的 Entrys,为每个 Entry 实例化一个对应的 EntryPlugin,为后面该 Entry 的递归解析工作做准备。

编译阶段:

compiler.hooks.beforeRun


compiler.hooks.run
启动一次新的编译
compiler.hooks.runRun run 类似,区别在于它是在监听模式下启动的编译,在这个事件中可以获取到是哪些文件发生了变化导致重新启动一次新的编译。


compiler.hooks.beforeCompile
compiler.hooks.compile 告诉插件一次新的编译将要启动,同时会给插件带上 compiler 对象
创建compilation实例 开发模式下,每当检测到文件变化,就会创建一个compilation,compilation包含本次编译的所有信息,包含了当前的模块资源、编译生成资源、变化的文件等
compiler.hooks.make Entry 开始读取文件,根据文件类型和配置的 Loader 对文件进行编译,编译完后再找出该文件依赖的文件,递归的编译和解析。


compiler.hooks.afterCompile

输出阶段:



compiler.hooks.shouldEmit
所有需要输出的文件已经生成好,询问插件哪些文件需要输出,哪些不需要。


compiler.hooks.emit
确定好要输出哪些文件后,执行文件输出,可以在这里获取和修改输出内容。


compiler.hooks.assetEmitted
compiler.hooks.afterEmit 文件输出完毕。


compiler.hooks.done
完成一次编译和输出

四、Loader

在上面的webpack简单配置例子中,已经展示了babel-loader的基本使用方法,他是将JavaScript进行一些转换,以便输出后的代码能在浏览器中很好的运行

loader的职责:loader的职责比较单一,它是将输入的源代码转换成你需要的目标代码,如将ts转换成js。

如何编写一个loader


五、Plugin

plugin在webpack中非常重要,我们可以在webpack的各种hooks上注册plugin,当webpack触发相应hooks时,便可执行plugin的内容。例如

  1. class MyPlugin {
  2. /**
  3. * Apply the plugin
  4. * @param {Compiler} compiler Webpack Compiler
  5. * @returns {void}
  6. */
  7. apply(compiler) {
  8. compiler.hooks.emit.tap(
  9. "MyPlugin",
  10. (compilation) => {
  11. // TODO: 插件的操作
  12. }
  13. );
  14. }
  15. }

在初始化阶段将MyPlugin注册在hooks.emit钩子上,等到webpack完成编译、确定要输出哪些资源的时候触发compiler.hooks.emit,然后会执行插件的具体内容

如何编写一个Plugin