webpack实现原理

36 阅读2分钟

好的,我来帮你系统梳理一下 Webpack 的实现原理。Webpack 本质上是一个 模块打包工具,它的核心思想就是:

👉 一切资源皆模块,通过 依赖图(dependency graph) 把项目中各个模块串起来,最终打包成浏览器可运行的 bundle。


1. 核心流程

Webpack 的运行可以分为 初始化 → 编译 → 输出 三个阶段:

(1) 初始化

  • 读取 webpack.config.js 配置文件。
  • 初始化 Compiler 对象(全局唯一,负责调度整个打包过程)。
  • 注册各种插件(plugins),把钩子函数挂载到生命周期。

(2) 编译(核心)

  1. 从入口开始
    Webpack 根据 entry 找到入口文件,调用 loader 对代码做转换(比如 TS → JS,CSS → JS)。

  2. 递归解析依赖

    • 使用 AST(抽象语法树)解析代码(依赖 @babel/parser 或 Acorn 等库)。
    • 找到 require / import 语句。
    • 把依赖的文件路径加入依赖图,继续递归编译。
  3. 构建依赖图(Dependency Graph)
    每个模块都会成为图中的一个节点,节点之间通过依赖关系连接起来。

  4. Loader 处理

    • Loader 本质是一个函数,接收源代码字符串,返回处理后的代码。
    • 可以链式执行,比如:scss-loader → css-loader → style-loader
  5. 插件介入
    插件通过 tapable 提供的钩子系统,在不同生命周期介入,例如:

    • compilation:模块编译阶段
    • emit:输出文件前
    • done:构建完成

(3) 输出

  • 根据依赖图,Webpack 把所有模块打包成一个或多个 bundle。
  • bundle 内部会有一个 模块加载器(runtime) ,负责模拟 require 的功能,保证模块能在浏览器中按需加载。

2. 核心原理拆解

🔹 (1) 依赖收集

  • Webpack 不认识 import,需要 AST 分析。
  • 通过 AST 遍历,找到依赖路径,递归处理。

例子(简化版 AST 解析):

import a from './a.js';
console.log(a);

Webpack 解析后会知道:main.js → a.js

🔹 (2) 模块包装

Webpack 会把每个模块包装成一个函数,放到对象里,类似:

{
  './src/index.js': function(require, module, exports) {
    const a = require('./a.js');
    console.log(a);
  },
  './src/a.js': function(require, module, exports) {
    module.exports = 'hello webpack';
  }
}

🔹 (3) 运行时代码(runtime)

打包后的 bundle 里有个简易版的 require

(function(modules) {
  function __webpack_require__(moduleId) {
    const module = { exports: {} };
    modules[moduleId].call(module.exports, __webpack_require__, module, module.exports);
    return module.exports;
  }
  return __webpack_require__('./src/index.js');
})({
  './src/index.js': function(require, module, exports) { ... },
  './src/a.js': function(require, module, exports) { ... }
});

这样浏览器里就能像 Node.js 一样运行模块了。


3. Loader 与 Plugin 原理

🔹 Loader

  • 本质是一个函数,输入源码字符串,输出新代码。
  • 支持链式调用,顺序是 从右到左
module.exports = function(source) {
  return source.replace('var', 'let');
}

🔹 Plugin

  • 基于 tapable 的发布订阅模式。
  • 插件通过钩子在不同阶段执行。
class MyPlugin {
  apply(compiler) {
    compiler.hooks.emit.tap('MyPlugin', compilation => {
      console.log('emit 阶段:生成文件前');
    });
  }
}

4. 总结一句话

Webpack 的核心原理就是:从入口文件出发 → 构建依赖图 → 把模块打包成一个 bundle,并通过 loader 转换资源、plugin 插入扩展能力,最终在浏览器里用 runtime 来模拟模块系统。


公众号二维码.jpg