Webpack 的 externals 配置可以让某些依赖不被打包进最终产物,而是在运行时从外部环境中获取。举个例子,你在项目里通过 import 引入了 jQuery,默认情况下 Webpack 会把 jQuery 的源码也打进 bundle 中,导致文件体积变大。但如果配置了 externals,Webpack 就会跳过对 jQuery 的打包,而是在代码运行时从全局变量 window.jQuery 或 window.$ 中读取。这样做的好处非常明显:减小打包体积、充分利用浏览器缓存,还能避免同一个库被多次重复打包。

externals 的概念并不复杂,但配置细节却有很多值得注意的地方。本文会从基础用法开始,逐步深入到高级配置,并结合实际场景给出最佳实践。看完之后,你就能清晰地知道哪些依赖适合用 externals 排除,以及如何避免一些常见的坑。
什么是 externals 以及它解决什么问题
简单来说,externals 就是告诉 Webpack:“这个模块不要打包,运行时从外部拿。”配置了 externals 之后,Webpack 在解析模块时如果遇到匹配的模块名,就会把它当作外部依赖处理,不再递归解析其内部代码。最终生成的代码里,对该模块的引用会变成从全局变量、CommonJS 的 require、AMD 的 define 或者 ES Module 的 import 等外部方式获取。
最典型的应用场景是使用 CDN 引入第三方库。比如你在 HTML 中先通过 <script src="https://cdn.ipipp.com/jquery.min.js"></script> 加载了 jQuery,那么在 Webpack 配置里就可以把 jquery 设为 external,这样业务代码中的 import $ from 'jquery' 在打包后就会直接使用已经存在的 window.jQuery,而不会把 jquery 的源码打进 bundle。
为什么需要这么做?几个主要原因:第一,减少打包体积,加快构建和加载速度;第二,利用浏览器缓存,第三方库版本稳定,单独通过 CDN 缓存可以避免每次业务代码更新都重新下载;第三,有些库不适合被打包,比如包含二进制文件或者依赖 Node.js 环境的模块。不过要注意,externals 只适用于那些能够在运行时从外部获取的模块,如果外部环境没有提供,运行时会直接报错。
// webpack.config.js
module.exports = {
// ...
externals: {
jquery: 'jQuery'
}
};
externals 的配置语法与模块化差异
externals 的配置非常灵活,支持字符串、对象、函数、正则等多种形式。最简单的字符串形式表示模块名和外部全局变量名相同,比如 externals: 'lodash' 表示打包时遇到 import _ from 'lodash',运行时就从全局变量 lodash 读取。对象形式则可以指定模块名到全局变量的映射,例如 { jquery: 'jQuery' } 表示模块名是 jquery,运行时从 jQuery 全局变量读取。
Webpack 5 引入了 externalsType 配置,用来指定外部依赖的加载方式,常见的值有 var(默认,全局变量)、commonjs、commonjs2、amd、module 等。不同类型的 externals 会影响最终生成代码的引用方式。例如配置 externalsType: 'commonjs' 后,外部模块会通过 require('xxx') 引入,而不是从全局变量读取。这在编写 Node.js 库时非常有用。
当使用函数形式的 externals 时,你可以根据请求的模块名动态返回外部变量名或配置对象。比如想让所有以 react- 开头的模块都从全局变量中读取,可以这样写:
// webpack.config.js
module.exports = {
// ...
externals: [
function ({ request }, callback) {
if (/^react-/.test(request)) {
// 返回外部变量名,比如 react-dom 对应 ReactDOM
return callback(null, 'root ' + request.replace('-', ''));
}
callback();
}
]
};
需要注意的是,externals 的全局变量映射是与模块化规范相关的。如果你的库设置了 output.libraryTarget: 'umd',那么 externals 的行为也会有所变化,可能会同时处理多种模块化环境。因此在使用 externals 时,务必先明确你的构建目标。
常见场景与最佳实践
最常见的 externals 使用场景是配合 CDN 优化前端性能。例如一个 React 项目,你可以把 react 和 react-dom 通过 CDN 引入,然后在 Webpack 配置中把它们设为 external。这样业务代码打包后不会包含 React 源码,体积会大幅减小。同时,由于 CDN 域名通常有长缓存策略,用户首次访问后,后续加载这些库几乎不消耗时间。
配置方法如下:在 HTML 中通过 <script> 标签引入 React 的 UMD 包,然后在 Webpack 配置中添加:
// webpack.config.js
module.exports = {
externals: {
react: 'React',
'react-dom': 'ReactDOM'
}
};
另一个常见场景是开发 Node.js 服务端代码。比如你在写一个需要依赖某些原生模块的工具库,但又不希望这些原生模块被打包进 bundle 中,就可以使用 externals 把它们排除,然后在运行时通过 require 动态加载。常见配置是 externalsPresets: { node: true },它会自动把 Node.js 内置模块以及 node_modules 中的依赖都排除出去。
使用 externals 时有一些坑需要留意。首先,必须保证外部环境在代码执行前已经提供了对应全局变量,否则会报 ReferenceError。其次,如果模块名和全局变量名不一致,要仔细核对映射关系,尤其是通过 CDN 引入的库,其全局变量名可能与 npm 包名不同。另外,externals 和 DllPlugin 都可以减少打包体积,但 externals 更简单直接,适用于第三方库稳定且适合通过 CDN 引入的场景;DllPlugin 则更适合内部模块拆分,但配置更复杂。
最后,使用 TypeScript 时,externals 不会影响类型检查,你仍然需要安装对应的类型定义包。同时建议在 ESLint 中配置 import/no-extraneous-dependencies 规则,避免因为模块不在 package.json 中而产生误报。

