免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

Rolldown 代码分割(Code Splitting)esbuild 兼容性快照测试全景:23 个 splitting 用例深度解析

Rolldown 代码分割(Code Splitting)esbuild 兼容性快照测试全景:23 个 splitting 用例深度解析 Rolldown 代码分割Code Splittingesbuild 兼容性快照测试全景23 个 splitting 用例深度解析【免费下载链接】rolldownFast Rust bundler for JavaScript/TypeScript with Rollup-compatible API.项目地址: https://gitcode.com/GitHub_Trending/ro/rolldown导读本文以 Rolldown 仓库中 esbuild 快照差异测试的splitting类别汇总文档为核心完整梳理该类别下 23 个通过用例的测试结构与技术内涵。通过逐一映射测试夹具_config.json、源码与artifacts.snap并结合 汇总生成器 runner.ts 与codeSplitting配置项的实现源码你将掌握Rolldown 如何以 esbuild 输出为基准验证自身代码分割行为共享模块抽取、动态导入拆包、CJS/ESM 互操作、跨 chunk 循环引用等场景的期望输出长什么样以及这份汇总文档是如何被自动化生成的。一、这份汇总文档是什么esbuild 快照差异测试中的 splitting 类别Rolldown 维护了一套以 esbuild 测试用例为蓝本的快照差异snap-diff测试体系用于衡量自身打包结果与 esbuild 输出的一致性。每个 esbuild 测试类别对应一份生成式的 Markdown 汇总文档splitting.md 就是其中面向**代码分割code splitting**类别的汇总。文档结构分为四个区块# Failed Cases失败用例当前为空# Passed Cases通过用例23 个全部链接到 crates/rolldown/tests/esbuild/splitting/ 下的真实测试夹具目录# Ignored Cases忽略用例当前为空# Ignored Cases (not supported)暂不支持而忽略当前为空。也就是说在这份文档所代表的快照比较范围内splitting 类别当前是 23/23 全部通过、零失败、零忽略的状态。1.1 汇总文档的生成机制这份文档并非手写而是由 runner.ts 自动生成的。关键逻辑如下runner.ts对每个 esbuild 快照类别执行快照对比得到diffList并按用例名排序调用getSummaryMarkdownAndStats(diffList, snapCategory)生成 Markdown 与统计信息写入snap-diff/summary/类别.md即本文对应的 splitting.md汇总所有类别的 pass / failed / ignored / total 统计。在 runner.ts 中可以看到分类与排版逻辑diffList按isNotSupported→isIgnored→isFailed→ 其余为passList的顺序归类Failed区块链接到每个夹具目录下的diff.md失败差异详情Passed区块则直接链接到夹具目录本身。此外生成器对“未记录原因的跳过用例”有硬性约束凡是以.前缀跳过skipped的用例都必须在 reasons.ts 中登记进ignoreReasons、notSupportedReasons或failedReasons否则直接抛出错误提示补全文档。这意味着 splitting.md 中“无忽略、无失败”的干净状态本身就是被工具链强制审计过的结论。二、23 个通过用例全景完整列表以下完整继承汇总文档中的全部链接并统一转换为仓库根目录相对路径。每一个链接都对应 crates/rolldown/tests/esbuild/splitting/ 下一个可独立运行的测试夹具目录edge_case_issue2793_with_splittingedge_case_issue2793_without_splittingsplitting_assign_to_localsplitting_chunk_path_dir_placeholder_implicit_outbasesplitting_circular_reference_issue251splitting_cross_chunk_assignment_dependenciessplitting_cross_chunk_assignment_dependencies_recursivesplitting_duplicate_chunk_collisionsplitting_dynamic_and_not_dynamic_common_js_into_es6splitting_dynamic_and_not_dynamic_es6_into_es6splitting_dynamic_common_js_into_es6splitting_dynamic_es6_into_es6splitting_dynamic_import_issue272splitting_dynamic_import_outside_source_tree_issue264splitting_hybrid_esm_and_cjs_issue617splitting_minify_identifiers_crash_issue437splitting_missing_lazy_exportsplitting_nested_directoriessplitting_public_path_entry_namesplitting_re_export_issue273splitting_shared_common_js_into_es6splitting_shared_es6_into_es6splitting_side_effects_without_dependencies从命名与测试语义看这批用例大致覆盖以下几个主题共享模块抽取splitting_shared_es6_into_es6、splitting_shared_common_js_into_es6动态导入拆包splitting_dynamic_es6_into_es6、splitting_dynamic_common_js_into_es6以及动态/静态混用的splitting_dynamic_and_not_dynamic_es6_into_es6、splitting_dynamic_and_not_dynamic_common_js_into_es6跨 chunk 依赖splitting_cross_chunk_assignment_dependencies及其递归变体、splitting_assign_to_local、splitting_side_effects_without_dependencieschunk 命名与路径splitting_nested_directories、splitting_public_path_entry_name、splitting_chunk_path_dir_placeholder_implicit_outbase历史 issue 回归issue251循环引用、issue272动态导入、issue264源树外动态导入、issue273重导出、issue437压缩标识符崩溃、issue617ESM/CJS 混合、issue2793含/不含分割两种开关。三、测试夹具的构成与运行方式以 splitting_shared_es6_into_es6 为例一个夹具目录通常包含四类文件_config.json打包配置入口定义a.js、b.js、shared.js等输入源码artifacts.snap期望输出快照insta 快照其头部注明source: crates/rolldown_testing/src/integration_test.rs说明由集成测试框架产出部分用例还带有compile-log.txt如 splitting_missing_lazy_export等附加产物。3.1 入口配置_config.json_config.json中的config.input采用“命名入口”数组形式name决定产物 chunk 名import指向相对源码{ config: { input: [ { name: a, import: a.js }, { name: b, import: b.js } ] } }这正是 splitting_shared_es6_into_es6 的配置两个入口共享同一模块。类似地splitting_nested_directories 用pageA/page.js、pageB/page.js两个嵌套目录下的入口入口名分别命名为pageA_page、pageB_page用于验证嵌套目录场景下的 chunk 命名规则。3.2 期望输出artifacts.snap快照直接呈现打包后的可执行代码本文所有产物代码均引自对应artifacts.snap文件。例如 splitting_shared_es6_into_es6/artifacts.snap 中的输出为// a.js console.log(123); // b.js console.log(123);输入中两个入口都import { foo } from ./shared.js并console.log(foo)而shared.js中export let foo 123。快照输出显示常量foo在分割场景下被求值内联为字面量123shared.js作为共享模块被消除无需单独成 chunk这与 esbuild 在该场景下的输出基准一致。四、代表性用例源码级拆解4.1 共享模块抽取splitting_shared_es6_into_es6输入a.js、shared.js// a.js import { foo } from ./shared.js console.log(foo) // shared.js export let foo 123a.js与b.js的输入完全一致。期望快照见上文显示两个入口各自输出console.log(123)。这说明在该场景下Rolldown 既完成了共享模块的识别又通过常量传播把导出值内联进了每个入口使共享 chunk 无需单独产出——是“分割 摇树/常量折叠”协同工作的典型结果。4.2 动态导入拆包splitting_dynamic_es6_into_es6splitting_dynamic_es6_into_es6 是单个入口、动态import()的场景。其 artifacts.snap 输出两个 chunk// entry.js import(./foo.js).then(({ bar }) console.log(bar)); // foo.js let bar 123; export { bar };入口保持import()的异步形态foo.js被拆为独立异步 chunk 并补上export { bar }动态导入的bar命名导出被保留在拆出的 chunk 中。这正是codeSplitting开启时动态导入默认被拆为独立 chunk 的行为验证。4.3 CJS 与 ESM 互操作splitting_dynamic_common_js_into_es6当动态导入的目标是 CommonJS 模块、输出格式为 ESM 时需要运行时 helper 做转换。splitting_dynamic_common_js_into_es6/artifacts.snap 展示了完整形态// entry.js import(./foo.js).then((m) /* __PURE__ */ __toESM(m.default)).then(({ default: { bar } }) console.log(bar)); export { __commonJSMin as t }; // foo.js import { t as __commonJSMin } from ./entry.js; var require_foo /* __PURE__ */ __commonJSMin(((exports) { exports.bar 123; })); export default require_foo();可以看到两条关键机制其一__commonJSMin这个 CJS 包装 helper 被提升到入口 chunk并通过export { __commonJSMin as t }与import { t as __commonJSMin }在 chunk 之间共享避免重复注入同时保证模块实例唯一其二动态导入处使用__toESM(m.default)将 CJS 的default归一化为 ESM 语义。快照头部还保留// HIDDEN [\0rolldown/runtime.js]标记说明运行时模块作为隐藏依赖参与打包。4.4 跨 chunk 循环引用splitting_circular_reference_issue251splitting_circular_reference_issue251 验证循环依赖模块被分割到不同 chunk 时的正确性// a.js var q 6; // 来自 b.js var p 5; // 来自 a.js export { p, q }; // b.js import { p, q } from ./a.js; export { p, q };b.js的内容var q 6被折叠进a.jschunk而a.js再统一export { p, q }b.js只保留对a.js的转发导入。即循环引用不破坏模块初始化顺序chunk 间的 import/export 边被正确重建。4.5 跨 chunk 赋值依赖splitting_cross_chunk_assignment_dependenciessplitting_cross_chunk_assignment_dependencies/artifacts.snap 覆盖“chunk A 修改、chunk B 读取共享状态”的场景// a.js import { t as setValue } from ./shared.js; setValue(123); // b.js import ./shared.js; // shared.js var value; function getValue() { return value; } function setValue(next) { value next; } sideEffects(getValue); export { setValue as t };共享模块shared.js被抽为独立 chunka.js通过import { t as setValue }使用内部重命名的导出b.js以副作用导入方式引入同一共享 chunk。其递归变体 splitting_cross_chunk_assignment_dependencies_recursive 则进一步覆盖多层依赖链下的情形。4.6 重复 chunk 冲突消除splitting_duplicate_chunk_collisionsplitting_duplicate_chunk_collision 有 4 个入口a、b、c、da/b都导入ab.jsc/d都导入cd.js。其 artifacts.snap 表明共享模块被去重合并ab.js与cd.js各自只产出一个共享 chunk4 个入口分别引用它们。用例名中的 “collision” 指向 esbuild 测试中关于重复/冲突 chunk 的语义这里验证的是去重后不产生命名冲突。4.7 嵌套目录入口命名splitting_nested_directoriessplitting_nested_directories/artifacts.snap 输出pageA_page.js、pageB_page.js两个入口 chunk// pageA_page.js console.log(123); // pageB_page.js console.log(-123);输入中pageA/page.js与pageB/page.js各自独立无共享模块因此直接按入口名pageA_page/pageB_page产出两个独立 chunk验证了命名入口在嵌套目录下的 chunk 命名拼接规则。4.8 输出路径占位符splitting_chunk_path_dir_placeholder_implicit_outbasesplitting_chunk_path_dir_placeholder_implicit_outbase/artifacts.snap 覆盖[dir]占位符与隐式 outbase 语义// entry.js console.log(import(./file.js)); // file.js位于 output-path/should-contain/this-text/ 下 console.log(file.js);入口通过动态import(./file.js)触发拆包动态 chunk 被放置到output-path/should-contain/this-text/路径下chunk 路径的//#region注释直接记录了该虚拟输出路径验证了目录占位符按模块相对隐式 outbase 的规则展开。4.9 空模块命名空间与缺失导出splitting_missing_lazy_exportsplitting_missing_lazy_export 的 common.js 是import * as ns from ./empty.js export function foo() { return [ns, ns.missing] } export function bar() { return [ns.missing] }该用例验证对空模块做命名空间导入并访问不存在的导出ns.missing时打包输出与 esbuild 基准一致——此类“惰性导出缺失”场景不会导致崩溃或错误产物目录中的compile-log.txt记录对应编译期输出。4.10 历史 issue 回归组splitting_dynamic_import_issue272动态导入相关的历史回归splitting_dynamic_import_outside_source_tree_issue264源树之外的动态导入路径处理splitting_re_export_issue273chunk 间重导出splitting_minify_identifiers_crash_issue437开启压缩标识符minify identifiers时的崩溃回归splitting_hybrid_esm_and_cjs_issue617同一打包中 ESM 与 CJS 模块混合edge_case_issue2793_with_splitting / edge_case_issue2793_without_splitting同一边界用例在“开启/关闭分割”两种开关下的对照验证其配置均为单入口index.js。这些以issueNNN命名的用例说明该类别不仅是功能测试更是一组针对真实缺陷的回归防线——任何一次快照变化都会被 runner.ts 捕获并反映到汇总文档中。五、与公开codeSplitting选项的源码映射这批测试所验证的“代码分割开关”对应的是 Rolldown 公开 API 中的codeSplitting选项。从源码看其内部模型是 code_splitting_mode.rs 中定义的CodeSplittingMode枚举Bool(true)默认行为自动代码分割惰性加载的动态导入被拆为独立 chunk对应splitting_dynamic_es6_into_es6等场景Bool(false)关闭分割所有动态导入内联进单一 bundleAdvanced(ManualCodeSplittingOptions)开启自动分割同时携带用户定义的手动分组manual chunk grouping配置。该枚举还提供is_automatic()/is_disabled()判定方法Default实现为Bool(true)。而选项字段本身定义在 inner_bundler_options/mod.rs/// Mirrors the public codeSplitting: boolean | CodeSplittingOptions. pub code_splitting: OptionCodeSplittingMode,其注释明确说明对象形式Advanced携带的分组配置在归一化normalization阶段被拆分为“开关门gate”NormalizedBundlerOptions::manual_code_splitting。这也解释了为什么 esbuild 测试用例中无需显式配置分组仅靠默认开关即可验证自动分割行为——高级分组属于另一层归一化逻辑。六、结论与边界说明把 splitting.md 与 crates/rolldown/tests/esbuild/splitting/ 下的夹具对照可以得到如下事实该快照类别当前全部通过23 个用例全部命中Passed CasesFailed Cases、Ignored Cases、Ignored Cases (not supported)均为空覆盖维度完整共享模块抽取、动态导入拆包、CJS/ESM 互操作与运行时 helper 注入、跨 chunk 循环引用与赋值依赖、chunk 命名与路径占位符、空模块缺失导出以及多个历史 issue 回归均有对应夹具结论边界这里的“通过”指 Rolldown 的输出与 esbuild 在该类别快照基准下的期望输出一致是快照比较层面的兼容性证据该汇总文档由 runner.ts 自动生成并强制审计跳过原因是持续集成中观察代码分割行为漂移的可靠入口。对于想深入代码分割实现的读者可以从 crates/rolldown/tests/esbuild/splitting/ 的任一夹具入手配合 code_splitting_mode.rs 的选项模型以及测试框架入口 integration_test.rs快照头部标注的 source逐步展开阅读。【免费下载链接】rolldownFast Rust bundler for JavaScript/TypeScript with Rollup-compatible API.项目地址: https://gitcode.com/GitHub_Trending/ro/rolldown创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表