深色模式
Angular 9 于 2020 年 2 月 6 日正式发布,Ivy 渲染引擎成为默认选项。升级了一批内部项目后,来分享真实的体积变化数据和踩坑经验。
实际体积变化数据
官方数据有时过于乐观,这是我们内部几个项目升级后的实测结果:
| 项目规模 | 升级前 main.js | 升级后 main.js | 减少 |
|---|---|---|---|
| 小型(20 组件) | 187KB | 131KB | 30% |
| 中型(80 组件) | 412KB | 268KB | 35% |
| 大型(200+ 组件) | 891KB | 671KB | 25% |
大型项目减少比例相对小,因为业务代码占比更高,框架代码的 tree-shaking 收益被稀释。
升级步骤
bash
# 一键升级(推荐)
ng update @angular/core@9 @angular/cli@9
# CLI 会自动:
# 1. 更新 package.json 依赖版本
# 2. 运行 migration schematic(处理 ViewChild static、字符串 loadChildren 等)
# 3. 运行 ngcc 编译第三方库升级后常见问题排查
1. ExpressionChangedAfterItHasBeenCheckedError 增多
Ivy 的变更检测更严格。常见原因是在 ngAfterViewInit 里修改了父组件绑定的值:
typescript
// ❌ 触发错误
ngAfterViewInit() {
this.title = 'loaded'; // 父组件模板绑定了 title
}
// ✅ 用 setTimeout 或 ChangeDetectorRef 解决
constructor(private cdr: ChangeDetectorRef) {}
ngAfterViewInit() {
setTimeout(() => {
this.title = 'loaded';
this.cdr.detectChanges();
});
}2. 第三方组件库样式异常
Ivy 改变了组件样式封装的实现方式,某些依赖 .mat-xxx 等全局类的样式写法可能失效:
scss
// ❌ 在宿主组件样式里穿透子组件(Ivy 下可能不生效)
:host ::ng-deep .mat-input-element {
color: red;
}
// ✅ 在全局样式(styles.scss)里覆盖
.my-custom-input .mat-input-element {
color: red;
}3. 动态组件创建 API 变化
typescript
// View Engine 方式(仍兼容但不推荐)
const factory = this.resolver.resolveComponentFactory(MyComponent);
this.container.createComponent(factory);
// Ivy 推荐方式(Angular 9+ 支持)
this.container.createComponent(MyComponent);新增的 providedIn 选项
typescript
// Angular 9 新增 'any' 和 'platform' 选项
@Injectable({ providedIn: 'any' })
// 'any': 每个懒加载模块获得独立实例,根模块共享一个实例
@Injectable({ providedIn: 'platform' })
// 'platform': 多个 Angular 应用之间共享同一实例(微前端场景)TestBed 性能提升
Angular 9 的 TestBed 在 Ivy 下不再每次都重新编译组件,测试套件运行速度显著提升:
typescript
// 不变的测试代码,但执行速度快了 2-3x
describe("UserComponent", () => {
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [UserComponent],
}).compileComponents();
});
// ...
});总结
Angular 9 是近年来对开发者最友好的大版本升级——绝大多数项目可以无痛升级,体积和性能都有实质改善。如果你还在 Angular 8 上,现在就是升级的好时机。