JavaScript模块加载器解决import无法运行时决定路径、按需加载及兼容非ES模块的问题;现代用import()动态导入,SystemJS用于加载AMD/UMD等旧模块,RequireJS等已过时。

JavaScript 模块加载器到底在解决什么问题
它不是语法层面的必需品,而是为了解决 import 无法在运行时决定加载路径、无法按需加载、无法兼容非 ES 模块(如 AMD、CommonJS)或无模块环境的问题。现代浏览器原生支持 import() 动态导入,但模块加载器(如 RequireJS、SystemJS、甚至 Webpack 的 runtime)提供更细粒度的控制:比如加载前重写路径、注入 polyfill、处理循环依赖、支持插件化转换(如 JSON、CSS 加载)。
用 import() 实现最简动态加载
这是目前标准、轻量、无需额外库的方式,返回 Promise,适用于大多数现代场景:
const loadModule = async (path) => {
try {
const mod = await import(path);
return mod.default || mod;
} catch (err) {
console.error('动态加载失败:', err);
throw err;
}
};
// 使用示例
loadModule('./utils/math.js').then(math => math.add(2, 3));
-
path必须是字符串字面量或模板字符串中的静态部分(如`./features/${feature}.js`),不能是完全运行时拼接的变量,否则打包工具(如 Vite、Webpack)无法做静态分析和代码分割 - 不支持直接加载 CDN 上的未配置 CORS 的脚本(会触发跨域错误),需服务端代理或确保目标资源设置
Access-Control-Allow-Origin - 多次
import('./x.js')同一路径,浏览器会复用已解析/执行的模块实例,不是重复请求
SystemJS:需要兼容旧模块格式时的选择
当你必须加载 AMD、UMD 或全局变量挂载型脚本(比如某些老版 Chart.js、Lodash CDN 版),import() 无能为力,SystemJS 是少数仍维护的通用加载器:
- 需显式配置
System.config({ map, packages })才能处理相对路径、版本别名或包入口 - 不推荐用于新项目 —— 它增加体积(~15KB gzip)、启动慢、与现代构建工具链割裂
- 常见坑:
System.import不是 Promise A+ 兼容实现,某些版本 resolve 值不统一;CDN 加载的模块若依赖其他 UMD 模块,需提前System.register声明依赖
为什么不用 RequireJS 或 SeaJS 了
它们是 AMD 规范时代的产物,核心逻辑基于 define/require 回调,而现代开发中:
立即学习“Java免费学习笔记(深入)”;
- ES 模块已是语言标准,
import()原生支持,无需学习新 API - 打包工具(Vite、Rollup、Webpack)内置代码分割,
import()能自动产出独立 chunk,RequireJS 的require(['a','b'], cb)在构建期无法优化 - RequireJS 的
shim配置容易出错:比如漏写exports导致模块值为undefined,或deps顺序错乱引发执行时序问题 - 几乎所有主流 UI 库(React、Vue、Svelte)及其生态都已转向 ESM 发布,RequireJS 加载它们需大量胶水代码
真正需要动态加载的复杂场景(如微前端、插件系统),通常用自定义 loader + import() 封装,而不是引入整套历史加载器。











