3.6 Angular CLI 高级应用 Angular CLI 不只是脚手架:多项目工作区、生成器原理、代理配置、构建预算与差分构建都由它管辖。本节把第 1 章一带而过的工程配置展开,并从主线视角看 CLI 的一个隐藏贡献——AOT 预编译把模板编译成变更检测代码,理解这一点,才算理解 Angular 构建与运行时的分界线。 学习目标 组织多项目工作区,说明应用工程与库工程的差别。 解释生成器(schematic)机制并定制一个自己的生成模板。 配置开发代理解决跨域,配置预算守住包体。 说明 AOT 与 JIT 的差别,以及 AOT 产物里"变更检测代码"的来源。 一、多项目工作区 工作区结构:根级配置统管依赖与全局规范,projects 目录下每个应用或库独立存在。
Angular CLI 不只是脚手架:多项目工作区、生成器原理、代理配置、构建预算与差分构建都由它管辖。本节把第 1 章一带而过的工程配置展开,并从主线视角看 CLI 的一个隐藏贡献——AOT 预编译把模板编译成变更检测代码,理解这一点,才算理解 Angular 构建与运行时的分界线。
# 工作区内新增第二个应用(如管理后台) ng generate application admin-portal # 新增可复用库(组件库、工具库) ng generate library @acme/ui-kit # 针对特定子项目执行命令 ng serve admin-portal ng build @acme/ui-kit
工作区结构:根级配置统管依赖与全局规范,projects 目录下每个应用或库独立存在。库工程的产物经部分编译(公开 API 的中间形态),供应用工程消费——第 4 章 Material 组件库正是这种形态的官方实现。多应用共享一个工作区的典型场景:面向用户的主站 + 内部管理台共用组件库与工具层。
依赖放置也有一条约定:只被单个应用用的依赖装在该应用的配置里,多个工程共享的依赖提升到根级——统一版本由根级锁住,避免两个应用各带一版底层库导致的隐性行为差异。这条约定配上一条检查:定期审视根级依赖清单,把"其实只有一个应用在用"的依赖下沉,工作区才不会越长越胖。
# 生成组件的完整参数示例 ng generate component orders/detail \ --project=admin-portal \ # 指定子项目 --change-detection=OnPush \ # 生成即 OnPush:把第 3.2 节的规范固化进模板 --standalone # standalone 组件
schematic 的本质是"代码生成配方":输入参数,按模板输出文件并登记修改。CLI 内置的生成器可定制——工作区的 schematics 配置允许把团队默认值固化(例如全局默认 OnPush、默认生成测试文件),新人敲一条命令得到的骨架就符合规范,而不是靠口头约定。
// 工作区配置中的生成器默认值示意 { "schematics": { "@schematics/angular:component": { "changeDetection": "OnPush", "standalone": true, "style": "scss" } } }
开发期跨域问题的正解是代理而不是放宽后端 CORS:
// 项目根的代理配置文件内容示意 { "/api": { "target": "http://localhost:8080", "changeOrigin": true, "pathRewrite": { "^/api": "/api" } } } // 启动:ng serve --proxy-config proxy.conf.json
构建预算在产物超限时失败或警告,守包体于无形:
{ "budgets": [ { "type": "initial", "maximumWarning": "500kb", "maximumError": "1mb" }, { "type": "anyComponentStyle", "maximumWarning": "4kb", "maximumError": "8kb" } ] }
⚠️ 坑:预算报错常被当成"碍事的限制"直接调大。正确姿势是先看是不是某次引入把大型依赖整个打了进来——预算是体温计,别掰断体温计治发烧。
预算的两个维度分别对应两种病:initial 管首屏包体,超标多半是懒加载切分不够(2.6 节的路由级拆分);anyComponentStyle 管单组件样式,超标往往是把全局主题样式复制进了组件。治法也不同:前者去拆路由与摇树,后者把样式上提到全局主题层——先读预算的类型,再决定动哪里,省得把两类问题混在一锅粥里搅。
JIT(运行时编译)时代,浏览器下载编译器,运行时才把模板编译成视图与检查指令;AOT(预编译)则把这一步挪到构建期:
ng build # 生产构建默认 AOT ng build --dev # 开发构建,仍走预编译但跳过优化
AOT 对主线的意义是最直接的:3.1 节说"每个组件在编译期生成自己的检查函数"——这些函数就是 AOT 产物的一部分。模板里每个绑定被编译成具体的更新指令(改文本、设属性、调管道),检查运行时执行的是这些编译产物,没有运行时解析模板这回事。三个连带收益:包里不再有编译器(体积)、模板错误构建期报(安全,呼应 3.4 节注入路径消失)、指令与管道按模板实际使用摇树(未导入的构件不进包,呼应 2.1 节 standalone 的组织逻辑)。

背景:团队推行 OnPush 与 standalone,但 code review 里总有新组件忘了加,规范靠人盯成本高。
操作:工作区配置生成器默认值(上文 schematics 片段);再写一个最小自定义 schematic,生成的组件附带一个"契约测试"骨架(呼应 3.3 节回归测试)。
结果:新组件从第一条命令起就符合规范,契约测试骨架随文件生成,review 焦点回归业务逻辑。
解读:CLI 是工程规范的自动化载体——能写进配置的约定不要写在文档里,文档会过时,配置就是执行。
变式:配合提交钩子(预提交跑 lint 与构建预算检查),把"体温计"从构建期提前到提交期,问题暴露越早修复越便宜。
下一节:PWA 与离线能力。