工程化?
当项目规模变大时,就会出现很多问题,这就是工程化要解决的问题。
出现的问题
全局污染问题
- 随着JS文件数量和代码量的增加
- 全局变量同名的几率将会陡然上升
- 开发人员不得不耗费大量的精力来规避
依赖混入问题
在js中先引入哪个模块,后引入哪个模块
模块化
用来解决全局污染和依赖混入问题
- 官方标准:ES Module
- 社区标准:CommonJS、AMD、CMD、UMD
| 特性 | AMD | CMD | UMD |
|---|---|---|---|
| 全称 | Asynchronous Module Definition | Common Module Definition | Universal Module Definition |
| 依赖声明 | 依赖前置(开头声明) | 依赖就近(用时 require) | 兼容多种方式 |
| 加载方式 | 异步 | 异步 | 根据环境决定(异步或同步) |
| 主要用途 | 浏览器模块化 | 浏览器模块化(已式微) | 通用库,多环境兼容 |
| 代表工具 | RequireJS | Sea.js | 无特定工具,是一种写法模式 |
| 是否主流 | 曾主流,现逐渐被 ES Module 取代 | 已不常用 | 仍广泛用于第三方库兼容 |
包管理
模块化出现后,出现了大量的第三方库,这么多第三方库,如何管理?
- npm
- yarn
- pnpm
Question
ES Module 库适配 CommonJS 环境
开发的 ESM 规范工具库(使用 export/import),下游用户基于 CommonJS 环境(使用 require/module.exports)无法直接引用,请问:
该问题的核心原因是什么?
ESM 和 CommonJS 是两种不兼容的模块化规范,ESM 依赖静态分析(export/import),CJS 是动态加载(require/module.exports),Node.js 无法直接用 require 加载纯 ESM 模块,会抛出 ERR_REQUIRE_ESM 错误
给出至少两种可行的解决方案,并详细说明实现步骤和优缺点。
- 方案 1:双格式打包。通过 Rollup/tsup 等工具将库同时打包为 ESM 和 CJS 格式,在 package.json 中用 main 声明 CJS 入口、module 声明 ESM 入口
- 方案 2:代码适配。在库入口文件中判断环境,同时通过 export 适配 ESM、module.exports 适配 CJS(仅适合简单库)
推荐最优方案,并解释理由。
双格式打包。理由:符合开源库最佳实践,对用户无感知,同时兼容 ESM/CJS 环境,可处理复杂依赖,扩展性强
JS 库适配低版本 Node.js 环境
封装的 JS 库仅适配 Node.js 24 版本,但公司老项目基于 Node.js 16,无法直接使用该库。请问:
核心适配难点是什么?
Node.js 24 与 16 存在 API / 语法差异(如 24 支持的新语法、内置 API,16 可能未实现或行为不同),且依赖的运行时特性、模块兼容逻辑也存在版本鸿沟
给出两种可行解决方案(简要说明);
- 方案 1:代码降级兼容。使用 Babel 转译新语法,通过 polyfill 补充缺失 API,移除 Node.js 24 专属 API,确保代码在 16 环境可运行
- 方案 2:版本分支维护。为老项目单独维护适配 Node.js 16 的库分支,核心逻辑同步更新,打包时指定目标环境为 Node.js 16
推荐最优方案并说明理由。
代码降级兼容。理由:无需维护多分支,通过工具统一适配,成本更低,且能一次性解决语法 / API 兼容问题,适配效率更高
ESM 与 CommonJS 选型决策
- 优先选 ESM:项目基于 Node.js 14.13.0+(ESM 支持成熟)、前端工程化项目(Vite/Webpack5+)、需使用顶级 await/ES 模块特性、追求静态分析 / 树摇优化、开源库需多环境兼容
- 选 CJS:老项目 / 依赖仅支持 CJS、Node.js 版本<14.13.0、需动态加载模块(如require(${path}/file.js))、依赖大量 CJS 专属模块(如__dirname未适配 ESM)
- Node.js 14.13.0 是 ESM 支持从「实验性」转向「稳定版」的关键版本
