JavaScript模块化关键是正确使用ES6 import/export:需script加type="module"、路径带扩展名;命名/默认导出语法须匹配;动态import()返回Promise用于按需加载;Node中混用CommonJS需注意双模式限制和createRequire。

JavaScript 模块化不是“要不要用”的问题,而是“怎么用对”的问题。ES6 模块(import/export)是当前标准方案,但直接在浏览器中运行、打包工具配置错误、循环依赖或动态路径写错,都会导致 Uncaught SyntaxError: Cannot use import statement outside a module 或 Module not found。
为什么 import 在 HTML 里直接写会报错
浏览器默认把 当作传统脚本执行,不启用模块解析。必须显式声明类型:
- HTML 中的
才启用 ES6 模块语法 -
type="module"脚本自动启用严格模式、顶层this为undefined、支持顶层await - 模块路径必须是相对路径(如
./utils.js)、绝对路径(/src/index.js)或完整 URL;不能省略扩展名(import { foo } from './utils'❌) - 非模块脚本无法
import,模块脚本也无法被(无type="module")导入
export 的三种写法和对应 import 方式
导出方式决定导入语法,混用会导致绑定失败或 undefined:
-
命名导出(named export):可多次,必须用大括号
import { foo, bar } from './mod.js';重命名用import { foo as myFoo } from './mod.js' -
默认导出(default export):每个模块最多一个,导入时名称任意:
import anything from './mod.js'(无需大括号) -
混合导出:允许同时有
export default和多个export const x = ...;导入时需分开处理:import def, { named } from './mod.js'
注意:export { foo as default } 不等于 export default foo —— 前者是命名导出的别名,后者是真正的默认导出,动态 import() 返回对象结构不同。
立即学习“Java免费学习笔记(深入)”;
动态 import() 解决什么问题
import() 是函数调用,返回 Promise,用于按需加载、条件加载或打破静态依赖图:
- 避免首屏加载大模块:比如只在用户点击“编辑”按钮后
import('./editor.js') - 根据环境导入不同实现:
const mod = await import(`./api/${API_ENV}.js`)(注意:路径不能是完全动态变量,需有静态前缀) - 绕过静态
import的限制:可在函数内、if块中、try/catch里调用 - 不触发 webpack 等工具的预打包——除非路径可静态分析,否则会单独生成 chunk
CommonJS 和 ES6 模块混用的坑
Node.js 14+ 支持双模式,但行为不一致:
-
require('./mod.mjs')会报错,因为.mjs强制 ES 模块;import无法直接加载.cjs或无type: "module"的.js -
exports和module.exports在 ES 模块中不可用;反过来,import不能在 CommonJS 文件中直接使用(除非用 Babel 或 Node 的--experimental-modules) - 第三方包若同时发布
exports字段中的import和require入口,要注意字段优先级(如"import">"require")
最稳妥的做法:项目统一用 type: "module" + .mjs 后缀,或全用构建工具(如 Vite、webpack)接管解析逻辑。手动混用时,createRequire(import.meta.url) 是 Node 环境下从 ES 模块中调用 require 的唯一合规方式。











