下文先用一段概要勾勒核心结论,再逐层剖析生成机制、文件职责、命名规律以及可验证的示例代码。
在一个由
Angular CLI
构建或ng serve
热重载的应用里,开发者通常会在浏览器Sources
面板看到形似e3f4c.vendor.js
与e3f4c.main.js
等文件。e3f4c
这一段其实是由output-hashing
机制按照 内容哈希(contenthash/chunkhash
)自动生成,用来实现精细的长效缓存;vendor
代表 第三方依赖代码块,main
代表 业务入口代码块。二者都由Angular CLI
内置的@angular-devkit/build-angular
构建器在 TypeScript → esbuild/webpack → 浏览器可执行文件 的流水线中自动拆分,并且在ng build
或ng serve
时机根据angular.json
中的vendorChunk
、optimization
、outputHashing
等选项确定是否拆分、何时注入哈希、最终写入dist/
或内存文件系统。
文件命名结构与 X
的含义
-
构建管线在生成最终脚本资源时,会为每一个 entry chunk 计算内容摘要;摘要通常被截断为 4-8 位十六进制字符,例如
e3f4c
。这一段对应问题中的X
,并随output-hashing
配置决定是否出现以及出现位置(stackoverflow.com, angular.dev)。 -
当
outputHashing
取值bundles
或all
(生产环境默认)时,每个 bundle 名都会带上哈希,以确保内容变化必然导致 URL 变化,浏览器即可对未变更的老文件做到“永久缓存”(reddit.com)。 -
在开发环境下若
outputHashing
设为none
,则编译器仅保留逻辑名称,不会带哈希,调试会更直观,也便于HMR
增量替换(github.com)。
vendor
块承担什么职责
-
vendor.js
由SplitChunksPlugin
(Webpack 5)或 esbuild 的splitting
逻辑按“来自node_modules
的依赖”规则抽取生成,专门放置 Angular 框架、RxJS、第三方 UI 组件库等不常变动的大体量代码(chitaranjanbiswal93.medium.com, justjeb.medium.com)。 -
把庞大的外部依赖与业务代码隔离,可使首次加载后浏览器对
vendor
建立独立 HTTP 缓存;只要依赖版本没变,后续上线仅更新main
,可极大降低增量发布流量(stackoverflow.com)。 -
在
angular.json
的build.options.vendorChunk
默认为true
;若显式改为false
,拆分逻辑会被关闭,依赖被内联入对应 entry(如main
),适合极简微应用或离线扩展场景(preemptive.com, angular.dev)。
main
块承担什么职责
-
main.js
由browser
入口(即src/main.ts
)经过AOT
/JIT
编译、tree-shaking
、ts-transpile
后产出,包含应用启动引导(platformBrowserDynamic().bootstrapModule(...)
)、所有惰性加载之外的业务组件、服务与样式引用(stackoverflow.com)。 -
当启用差分编译时,CLI 会为现代浏览器与旧版浏览器各生成一组
main-es20XX.js
与main-es5.js
文件,并在index.html
里通过<script type="module">
/<script nomodule>
标签自动分发(stackoverflow.com)。
生成时机、地点与流水线
-
触发命令:
ng build --configuration production
或ng serve
。前者把文件写入dist/project-name/
,后者使用内存dev-server
提供(angular.dev)。 -
编译阶段:
@angular-devkit/build-angular
根据angular.json
读取browser
target,调用esbuild
(Angular 17+)或 webpack(旧版)编译 TypeScript 及模板。 -
Chunk 拆分:
- esbuild 原生
splitting: true
配合format:"esm"
把依赖收敛进vendor
;webpack 旧链路则通过optimization.splitChunks.cacheGroups.vendor
规则实现(webpack.js.org)。
- esbuild 原生
-
哈希注入:构建器根据
outputHashing
把contenthash
追加到文件名;相同 chunk 内容不变时哈希保持不变,浏览器即可利用 304 缓存(stackoverflow.com)。 -
写入或注入:生产构建将物理文件输出到
dist/
;开发构建通过 Webpack dev-middleware 注入到内存,浏览器仍以 URL 形式访问。
可运行示例(Angular 17.x)
# 初始化项目
npm create @angular@latest sample-hash-demo -- --routing --style scss
cd sample-hash-demo
# 修改 angular.json,显式保留 vendor 块并打开所有哈希
npx jq '.projects["sample-hash-demo"].architect.build.options.vendorChunk=true |
.projects["sample-hash-demo"].architect.build.configurations.production.outputHashing="all"' \
angular.json > tmp && mv tmp angular.json
# 添加一个第三方依赖以观察 vendor 增长
npm i lodash-es
# 在任何组件里引用
echo "import pad from 'lodash-es/pad'; console.log(pad('hash',10,'-'));" >> src/main.ts
# 产出构建物
ng build --configuration production
执行完毕后,可在 dist/sample-hash-demo/browser/
目录看到类似下列文件清单:
e3f4c.vendor.js
e3f4c.vendor.js.map
e3f4c.main.js
e3f4c.main.js.map
e3f4c.polyfills.js
e3f4c.runtime.js
index.html
vendor
大小会因 lodash-es
明显增大,而 main
只收录业务组件与少量 bootstrap 代码;若再次修改业务组件但不变更依赖,重新构建后 main
的哈希将改变,而 vendor
的哈希保持 e3f4c
不变,恰好验证了长效缓存设计。
命名细节与调试提示
-
调试源码映射:CLI 总是在非优化模式下为
main
与vendor
生成.map
文件,便于 Chrome 对应到.ts
源文件(stackoverflow.com)。 -
哈希过长:可通过
namedChunks:true
保留原有名字(main.js
、vendor.js
),但生产环境并不推荐,因为会破坏缓存粒度(angular.dev)。 -
禁用 vendor 拆分:在极简脚本或扩展场景中,可将
vendorChunk:false
,此时所有第三方模块连同业务代码被并入main
,减少一次请求,但也失去了依赖层面的缓存优势(justjeb.medium.com, stackoverflow.com)。
参考线上资料
-
Medium 文章对各 bundle 职责的解释(chitaranjanbiswal93.medium.com)
-
StackOverflow 对
main.js
、vendor.js
的条目式说明(stackoverflow.com) -
Medium 讨论
vendorChunk
开关及性能影响(justjeb.medium.com) -
Webpack 官方文档
SplitChunksPlugin
关于默认命名规则解释(webpack.js.org) -
StackOverflow 关于是否在生产中启用
vendorChunk
的探讨(stackoverflow.com) -
Angular 新构建系统迁移指南指出已弃用
vendorChunk
选项(angular.dev) -
JSDefender 文档示例展示如何在 Angular 中使用
--vendorChunk=true
(preemptive.com) -
CLI 文档
ng build
关于outputHashing
的说明(angular.dev, angular.dev) -
StackOverflow 回答总结四种
--output-hashing
模式(stackoverflow.com) -
Reddit 讨论说明哈希一致性与缓存关系(reddit.com)
-
GitHub issue 展示差异化构建产物包含
es2020
与es5
文件(stackoverflow.com) -
Split vendor 方案及性能讨论(justjeb.medium.com)
-
Webpack issue 关于默认命名
vendors~main.js
的描述(webpack.js.org) -
Angular CLI serve 旧文档里
--vendor-chunk
开关(angular.ossez.com) -
粗略文件清单与输出示例 StackOverflow 讨论(chitaranjanbiswal93.medium.com, stackoverflow.com)
这些来源交叉印证了命名规则、拆分策略与缓存原理,使得开发者可以在浏览器调试界面迅速判断脚本职责、找准潜在优化点。