JavaScript模块打包器通过静态分析import/require等语句构建依赖图,不执行代码故无法识别动态引入;Webpack用acorn解析AST,Rollup更严格仅处理ESM,Vite用esbuild预解析但不处理require和非JS资源。

JavaScript模块打包器不是靠猜来解析依赖的,它通过静态分析源码中的 import、require()、define() 等语句,构建出一张明确的依赖图。动态 require()(如 require(path))或运行时拼接模块名的情况,大多数打包器默认不处理——这不是缺陷,而是设计取舍。
Webpack 怎么扫描 import 和 require 语句
Webpack 的 Parser 在编译阶段对每个模块源码做 AST 解析(用的是 acorn),提取所有顶层 import 声明和 require() 调用。它不执行代码,所以无法识别 if (cond) require('./a') 这类条件引入,除非你显式配置 require.context()。
-
import语句全部被静态捕获,包括命名导入、默认导入、重命名、类型导入(import type)会被忽略 -
require('./foo.js')这种字面量路径能被准确解析;require('./' + name)会被跳过,触发ContextModule警告 - ESM 和 CommonJS 混写时,Webpack 默认把
require()当作 CommonJS 处理,但会尝试模拟 ESM 导出行为(需开启experiments.outputModule才能输出真正的 ESM)
Rollup 的依赖解析更严格,也更“纯”
Rollup 默认只处理 ESM 语法,它的依赖图是扁平且无副作用的——它会把 import { foo } from 'pkg' 编译成直接内联 foo 的定义,而不是保留一个运行时 require 调用。这也意味着它天然不支持动态 require 或 __dirname 这类 Node.js 特有变量。
- 不识别
require.resolve()、require.cache等运行时 API,遇到就报错 -
import()动态导入会被转为异步 chunk,但路径必须是字符串字面量,不能含变量 - 如果项目里混用了
export default和module.exports =,Rollup 可能漏掉导出,需用plugin-commonjs显式桥接
Vite 用 esbuild 预解析,但仍有边界
Vite 启动时用 esbuild 快速扫描 import 语句生成依赖列表(deps),这个过程极快,但 esbuild 不解析 require(),也不处理非 JS 资源(比如 import './style.css' 是 Vite 插件层接管的,不是 esbuild 干的)。
立即学习“Java免费学习笔记(深入)”;
- 开发服务器启动快,是因为 esbuild 只做最小必要解析,不走完整打包流程
-
import.meta.glob('./pages/**/*.vue')是 Vite 特有的语法糖,esbuild 完全不认识,靠插件在解析阶段重写为可识别的import()形式 - 如果你在
.ts文件里写了require('fs'),Vite 开发时可能不报错(Node.js 模块未被拦截),但构建时会失败——因为生产构建走的是 Rollup,而 Rollup 默认不处理 Node 内置模块
真正容易被忽略的是:依赖解析发生在构建前期,和代码是否能“跑通”无关。写一个语法合法但逻辑错误的 import(比如路径拼错、大小写不符、缺少扩展名),打包器照样能画出依赖图——只是后续解析模块内容时才暴露问题。路径别名、自动扩展名、条件导出这些能力,全靠配置驱动,不是自动生效的。










