javascript模块化开发是如何实现的?【教程】

8次阅读

javaScript模块化依赖明确规范与环境支持:ESM需type=”module”或.mjs后缀,import必须顶层静态声明;CommonJS仅node原生支持,浏览器需打包器转换;二者机制冲突,混用易出错。

javascript模块化开发是如何实现的?【教程】

javascript 模块化不是靠某一个“开关”或语法糖自动实现的,而是依赖明确的模块格式规范 + 构建工具或运行时环境支持。没有 import / export 就没有 ES 模块,没有 requiremodule.exports 就没有 CommonJS 模块——它们不是可选特性,是强制约定。

ES 模块(ESM)必须用 type="module".mjs 后缀启动

浏览器中直接写 import 会报错:Uncaught SyntaxError: Cannot use import statement outside a module。原因很简单:默认 html 脚本是 classic script,不解析模块语法。

  • 标签中启用 ESM,必须加 type="module" 属性
  • Node.js 中,要么文件后缀为 .mjs,要么在 package.json 中声明 "type": "module"
  • 不满足任一条件,import 会直接抛语法错误,且无法降级为 CommonJS
  • ESM 是静态分析的——import 必须在顶层,不能放在 if 或函数里;动态导入要用 import() 函数形式

CommonJS 只在 node.js 默认生效,浏览器里需要打包器

require()module.exports 是 Node.js 的原生模块系统,但浏览器根本不认识它们。想在浏览器用,必须借助 webpackvite、Rollup 等工具把 CommonJS 转成可执行代码。

  • Node.js v12+ 默认支持 ESM,但 CommonJS 仍是默认行为(除非显式开启 type: "module"
  • CommonJS 是运行时加载:require() 返回值可以是任意类型,支持循环引用(有缓存机制)
  • 混用 ESM 和 CommonJS 风险高:比如在 ESM 文件中 import 一个 CommonJS 模块,得到的是 default 属性包裹的对象,而非导出值本身
  • Vite 在开发时能处理混合导入,但生产构建可能出问题,尤其涉及 __esModule 标记和 default 提取逻辑

importrequire 的加载时机与作用域完全不同

这不是风格差异,是底层机制冲突。ESM 是编译时静态链接,CommonJS 是执行时动态求值——这意味着它们对变量提升作用域封闭、副作用触发时机的处理完全相反。

  • import 声明不会被提升到块顶,但整个模块作用域会被提前解析(hoisting 是模块级的,不是语句级)
  • require() 是同步阻塞调用,会立即执行模块脚本并返回结果;而 import()异步 promise,适合按需加载
  • ESM 中的顶层 thisundefined;CommonJS 中是当前模块的 exports 对象
  • 试图在 ESM 中用 require(比如通过 module.require)会破坏模块图完整性,Vite/Webpack 通常不支持

真正难的不是写 export,而是理解不同模块系统之间如何桥接、何时该用动态 import()、以及为什么某些库(比如早期的 Lodash)要提供 lodash-eslodash 两个版本——本质是导出格式与消费方式的耦合。没理清这点,哪怕语法全对,也会在 tree-shaking、循环依赖、服务端渲染时掉进坑里。

text=ZqhQzanResources