十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

使用 ES Modules(import/export)重构前端代码:从 CommonJS 迁移到原生模块规范

使用 ES Modules(import/export)重构前端代码:从 CommonJS 迁移到原生模块规范 使用 ES Modulesimport/export重构前端代码从 CommonJS 迁移到原生模块规范【免费下载链接】Front-End-Checklist The essential checklist for modern web development, for humans and AI agents项目地址: https://gitcode.com/gh_mirrors/fr/Front-End-Checklist本指南以 Front-End-Checklist 仓库中的es-modules规则高优先级、入门难度、约 15 分钟为核心系统讲解 JavaScript 原生 ES Modules 语法import/export如何取代 CommonJS 的require()/module.exports涵盖静态分析与 tree-shaking 原理、命名导出与默认导出的取舍、浏览器原生typemodule加载、动态导入代码分割、Barrel 再导出文件以及 TypeScript 与构建管线下的落地验证。读完你将掌握一套可立即套用的迁移与验收流程。规则速览在 es-modules 技能定义 中这条规则的定位是优先级high高——影响打包体积、加载性能与工具链质量难度beginner入门预估耗时15 分钟分类javascript核心主张使用原生 ES module 语法import/export替代 CommonJS 的require()以启用静态分析、tree-shaking 与更完善的工具支持。规则给出的快速参考要点如下用import/export语法替代require()/module.exports命名导出named exports改善 IDE 自动补全并支持 tree-shaking在浏览器中要么给script加typemodule要么使用打包器加载 ES modules默认导出default exports可用但大型代码库中命名导出扩展性更好。为什么 ES Modules 更重要静态可分析与 tree-shakingES modules 的核心优势在于静态可分析性statically analyzableimport/export语句的依赖关系在源码层面是显式、确定、可枚举的打包器bundler可以在构建期确定哪些代码真正被使用并把其余部分消除掉——这正是 tree-shaking 的前提。相比之下CommonJS 的require()是动态的——它可以在任意作用域、任意条件下、用任意表达式调用模块依赖图在构建期无法完整推断从而阻止了这项优化。另一个关键事实是ES modules 已是浏览器与 Node.js 的原生标准原生支持意味着无需再依赖构建期转换即可在现代环境直接运行同时天然受益于引擎级的模块缓存与严格模式语义。从仓库的规则体系看这条规则与其他 JavaScript 规则紧密关联代码分割规则 在relatedRules中明确指出Code splitting requires ES module syntax — dynamic import() is an ES feature代码分割需要 ES module 语法——动态import()是 ES 特性type-only-imports 规则 也把import type定义为 builds on ES module syntax and interacts with bundler tree-shaking behaviour建立在 ES module 语法之上并与打包器的 tree-shaking 行为交互。也就是说ES Modules 不是孤立的一种写法偏好而是代码分割、按需加载、类型安全导入等一系列现代前端优化的语法地基。代码对照从 CommonJS 迁移到 ES Modules原规则给出的迁移对照如下ES Modules 一侧的完整代码已补全// ❌ CommonJS新代码应避免 const utils require(./utils) const { formatDate } require(./utils) module.exports { myFunction } module.exports myFunction // ✅ ES Modules import utils from ./utils.js import { formatDate } from ./utils.js export { myFunction } export default myFunction迁移时的规则很简单把所有require()和module.exports语句转换为 ES module 的import/export语法。这也是技能中Check/Fix两个动作的定义——先识别 JavaScript 文件中需要转换的require()、module.exports、exports用法再逐一转换。本仓库自身的配置文件就是 ES module 语法的活例apps/web/next.config.js 通篇使用import { withContentCollections } from content-collections/next、import { withSentryConfig } from sentry/nextjs以及export default withContentCollections(sentryWrappedConfig)而type {import(next).NextConfig}这类 JSDoc 类型导入同样依赖 ESM 语法。命名导出与默认导出如何选择规则强调命名导出与默认导出的适用场景差异// utils.js — 命名导出库与工具函数首选 export function formatDate(date) { return date.toISOString().slice(0, 10) } export function formatCurrency(amount) { return new Intl.NumberFormat(en-US, { style: currency, currency: USD }).format(amount) } // consumer.js import { formatDate, formatCurrency } from ./utils.js // 只有被导入的函数会进入产物包tree-shaking 生效 console.log(formatDate(new Date()))// UserCard.js — 默认导出适用于单一概念的模块 export default function UserCard({ user }) { return article${user.name}/article } // consumer.js import UserCard from ./UserCard.js选择建议命名导出named exports是库与工具模块的首选IDE 可以精确补全导入名、静态检查可以校验拼写、打包器可以精确 tree-shaking——只有真正被导入的绑定才会被打进产物默认导出default exports适合一个模块表达一个概念的场景如一个组件文件对应一个组件但命名不统一时容易造成引用名漂移import X fromvsimport Y from在大型代码库中命名导出扩展性更好这也是技能 Quick Reference 的明确结论。在浏览器中直接使用typemodule不需要打包器时可以直接在 HTML 中使用原生模块脚本!-- 添加 typemodule 即可在浏览器直接使用 import/export -- script typemodule import { formatDate } from ./utils.js console.log(formatDate(new Date())) /script模块脚本具有三个区别于普通脚本的语义默认延迟执行deferred模块脚本不会阻塞 HTML 解析行为等价于defer自动严格模式模块代码以严格模式运行无需显式书写use strict独立作用域模块顶层声明的变量不会泄漏到全局window避免全局污染。动态导入与代码分割import()表达式允许在运行时按需加载模块是代码分割code splitting的语法入口// 仅在需要时加载重型模块 async function loadChart() { const { Chart } await import(./chart.js) return new Chart(document.getElementById(canvas)) } button.addEventListener(click, loadChart)仓库中的代码分割规则给出了更完整的工程化示例静态导入会把整棵依赖图拖入初始包而动态导入可以让 PDF 生成、图表库、富文本编辑器等重模块仅在用户交互时下载并执行// ❌ 静态导入即使从未使用也会加载 import { generatePDF } from ./pdf-generator.js // ✅ 动态导入仅在需要时加载 async function handleExportClick() { const { generatePDF } await import(./pdf-generator.js) generatePDF(document) }在 React 应用中动态导入常与lazy/Suspense配合实现路由级切包——每个路由一个 chunk用户只下载当前页面所需代码详见 code-splitting.mdx 的 Route-Based Splitting 示例。这就是es-modules规则被code-splitting规则列为前置条件的原因没有 ES module 语法动态import()便无从谈起。再导出与 Barrel 文件Barrel 文件桶文件通过再导出把多个子模块收敛到一个中心入口简化调用方的导入路径// index.js — 从中心入口再导出 export { formatDate, formatCurrency } from ./utils.js export { default as UserCard } from ./UserCard.js export * from ./validators.js三种再导出形式的语义区别export { a } from ./mod.js显式再导出命名绑定export { default as X } from ./mod.js给默认导出重命名后再导出export * from ./mod.js批量再导出全部命名导出不含默认导出。注意Barrel 文件本身也参与 tree-shaking但过深的 barrel 链会引入循环引用风险因此在大型代码库中应控制 barrel 的使用范围——仅对公共 API 面做收敛内部模块保持直接导入。与 TypeScript 的协作import type与模块解析配置ES module 语法与 TypeScript 的结合值得单独强调。仓库中的 type-only-imports 规则 指出即使只使用某个模块的类型interface/type普通import也会让运行时去求值该模块——增加包体积、引入副作用并可能引发循环依赖。正确做法是使用import type// ✅ 仅类型导入编译期完全擦除零运行时开销 import type { RuleMeta } from ./types.js // ❌ 若 RuleMeta 仅用于类型注解普通 import 仍会保留运行时模块求值 import { RuleMeta } from ./types.js仓库的 TypeScript 基础配置 configs/config-typescript/base.json 展示了与 ES module 语义对齐的编译器设置{ compilerOptions: { module: NodeNext, moduleResolution: NodeNext, isolatedModules: true, esModuleInterop: true, moduleDetection: force, target: ES2022 } }这些选项的实战意义module: NodeNextmoduleResolution: NodeNext按 Node.js 的 ESM/CJS 双模式语义解析模块强制开发者明确写全文件扩展名isolatedModules: true要求每个文件可被独立转译间接强制了import type等安全写法moduleDetection: force把所有文件当作模块处理避免全局脚本陷阱esModuleInterop: true允许在 ESM 中便捷地默认导入 CommonJS 模块import fs from node:fs。工作区根配置 tsconfig.base.json 通过extends继承该基础配置并补充repo/*路径映射——整个 monorepo 的 TypeScript 编译均在同一套 ESM 语义下进行。支持说明迁移时要注意的三个坑原规则的 Support Notes 给出了三条实战警示验证最终产物而非源码语法模块加载行为取决于目标浏览器矩阵和构建管线typemodule的原生支持、打包器的输出格式、module/moduleResolution配置等都会改变运行时行为所以必须检查最终 shipped 输出不能只看源码写法遗留目标需要显式回退方案如果兼容范围仍包含不支持原生 ESM 的老浏览器如 IE需要把降级fallback或转译路径如打包器输出 IIFE/UMD、nomodule回退脚本明确文档化严格模式并非处处自动生效ES modules 与 class 代码体自动处于严格模式但如果代码库仍有传统的非模块scriptuse strict指令在那里依然有意义——混合脚本环境下不要假设所有代码都处于严格模式。验证清单如何确认改动正确规则要求在浏览器中验证行为而不仅是静态分析并给出了两层检查手段。自动化检查代码变更后在浏览器中验证实际行为而非只看静态分析结果当规则影响加载或执行顺序时检查 DevTools 的 Network / Performance 面板确认模块脚本是否按预期 defer、chunk 是否按预期拆分与加载测试主用户流 一条由变更脚本路径触发的边缘用例。对应到本仓库的验证体系还可参考代码分割规则 的自动化检查建议用 bundle visualizer 对比前后产物、确认初始 chunk 显著缩小且import()真正把代码移出初始依赖图、用 Lighthouse 或性能预算复测初始 JS 成本常见起始阈值为主路由包 gzip 后 ≤ 150 KB。手动检查确认功能被延迟、懒加载或失败时比如网络中断导致动态 chunk 加载失败代码仍能正确降级——加载状态、Suspensefallback 或错误提示是否到位。小结ES Modules 不是一种可选的风格偏好而是现代 Web 的性能与工具链基石静态可分析性带来 tree-shaking原生浏览器支持降低转换负担动态import()打开代码分割的大门import type让类型导入零运行时成本。迁移时记住三条主线新代码一律import/export、浏览器侧用typemodule或打包器、TypeScript 侧用verbatimModuleSyntax/isolatedModules等配置把 ESM 语义固化到编译层。最后永远以最终浏览器产物作为验收标准。如需进一步深入可在本仓库中继续阅读es-modules 技能定义含 Agent 使用的 Check/Fix/Explain/Code Review 提示词、代码分割规则、type-only-imports 规则、Next.js 配置示例 与 TypeScript 基础配置。【免费下载链接】Front-End-Checklist The essential checklist for modern web development, for humans and AI agents项目地址: https://gitcode.com/gh_mirrors/fr/Front-End-Checklist创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表