JavaScript模块化核心在于统一ESM语法、明确模块边界与静态分析保障。需锁定type: "module",禁用CommonJS;优先命名导出,限制默认导出;按功能分层目录,杜绝循环依赖;启用eslint-plugin-import等规则确保路径正确、无重复导入、类型安全及导入顺序规范。

JavaScript模块化不是简单加个export就完事,关键在一致性、可维护性与团队协作的落地。核心是统一导入导出形式、明确模块边界、避免副作用,并让Lint工具自动守住底线。
统一使用ESM语法,禁用CommonJS混用
现代项目应锁定type: "module"(package.json中声明),全程使用import/export。禁止在同一个项目里出现require()或module.exports——它们与ESM不兼容,会导致动态导入失败、tree-shaking失效、类型推导断裂。
- 默认导出仅用于单例对象(如主组件、配置类),且一个文件最多一个:
export default MyComponent; - 命名导出优先于默认导出,尤其对工具函数、常量、类型:例如
export const API_URL = "..."; export function debounce() {...} - 禁止
export default function foo() {}这种写法——它让命名不可检索、重命名困难;改用export function foo() {}+ 单独export default foo;(如需默认)
模块职责清晰,禁止跨层/循环依赖
每个模块应有单一语义角色:要么是数据模型(types.ts)、要么是业务逻辑(userService.ts)、要么是UI封装(Button.tsx)。目录结构按功能分层(如features/auth/),而非技术类型(避免全项目堆utils/)。
- 入口文件(如
index.ts)只做聚合导出,不写业务逻辑 - 用
eslint-plugin-import开启no-cycle和no-useless-path-segments规则,拦截import ... from "../../.."和import ... from "./index" - 接口定义放在
types/或同级*.d.ts,不与实现耦合;避免import { X } from "./someModule";同时引入类型和值
静态分析必须覆盖的Lint关键规则
以下规则建议直接写入.eslintrc.cjs,配合TypeScript使用效果更佳:
立即学习“Java免费学习笔记(深入)”;
-
import/no-unresolved:确保所有路径存在,防止拼写错误导致运行时崩溃 -
import/named:导出名不存在时立即报错(如import { nonExistent } from "./a") -
import/no-duplicates:同一模块多次导入合并为一次,减少包体积 -
@typescript-eslint/no-explicit-any+no-unsafe-*系列:强制类型收敛,杜绝any滥用 -
import/order:规定导入顺序(builtin → external → internal → parent → sibling → index),提升可读性
构建与运行时的一致性保障
开发期用Vite或Webpack,生产环境也保持相同模块解析逻辑。禁用node: true(ESLint配置中),避免误将浏览器API当Node内置模块;若需兼容Node,显式添加env: { node: true }并限定作用域。
- 动态
import()只用于路由级代码分割或条件加载,禁止在循环/高频函数中调用 - JSON模块导入(
import data from "./data.json" assert { type: "json" };)需确认运行时支持,否则回退到fetch()+await response.json() - 所有
import.meta.env变量必须在.env中有对应定义,用import/no-unresolved配合eslint-plugin-import校验










