最近在使用 NestJs 和 NextJs 在做一个协同文档 DocFlow,如果感兴趣,欢迎 star,有任何疑问,欢迎加我微信进行咨询 yunmz777
如果说 Signals 是 Angular 的"顿悟"时刻,那么无 Zone(Zoneless)则是"这感觉真流畅"的时刻。在 Angular v21 中,新应用默认使用无 Zone 的变更检测方式,这意味着不再有 Zone.js 在幕后运行------只有通过 Signals 实现的明确、可预测的响应式编程。
简而言之:告别 Zone.js,迎来更小的包体积、更清晰的堆栈追踪,以及仅在您指定时(通过 Signals 和事件)更新的变更检测。
什么是无 Zone 模式?
从历史上看,Angular 使用 Zone.js 来修补异步 API(如定时器、事件、Promises)并自动触发变更检测。这种方式虽然方便,但有时会过于积极,导致不必要的检查。
无 Zone 模式完全移除了 Zone.js,转而依赖 Signals 和模板事件来确定何时更新 UI。这意味着:
- 变更检测只在明确需要时触发(Signals 变化、输入变化、模板事件)
- 不再有全局的异步操作监听
- 更少的运行时开销,更好的性能表现
结果是更少的工作量、更少的意外情况,以及更好的性能。
v21 的新特性
新项目默认采用无 Zone 模式
v20.2 稳定了该 API(provideZonelessChangeDetection()),而 PR #63382 使其成为 ng new 的默认设置。现有应用可以通过一行提供者代码选择加入。
更清洁的构建
在无 Zone 模式下,您的包中不再包含 Zone.js,这意味着:
- 更小的包体积(减少约 30KB)
- 更清晰的堆栈追踪
- 更少的运行时开销
SSR 和错误处理已就绪
在迈向无 Zone 的过程中,Angular 添加了强大的错误处理程序和 SSR 钩子,因此默认设置已可用于生产环境。
注意:v20.2 正式将无 Zone 提升为稳定版。v21 是第一个将其作为新应用默认设置的主要版本。
是否需要迁移现有应用?
简短回答:是的,建议计划迁移。您可能会看到更少的不必要检查和更流畅的思维模型------特别是如果您已经在使用 Signals。
迁移带来的收益
-
可预测性:变更检测在输入、Signals 或事件更改时运行,而不是"每当某处发生任何异步操作时"。您可以清楚地知道何时会触发变更检测。
-
性能提升:减少过度检查,减少修补操作带来的开销。应用运行更流畅,响应更快。
-
开发体验:更清晰的堆栈追踪,减少"这个变更检测从哪里来的?"这类困惑。调试更容易,问题定位更准确。
迁移指南
步骤 1:移除 Zone.js
从 angular.json 的 polyfills(构建和测试)中删除 Zone.js(包括 zone.js/testing)。如果您有 polyfills.ts 文件,请移除 import 'zone.js' 这一行。
步骤 2:启用无 Zone 的变更检测
在应用启动时添加无 Zone 变更检测提供者:
typescript
import {
bootstrapApplication,
provideZonelessChangeDetection,
} from "@angular/core";
import { AppComponent } from "./app/app.component";
bootstrapApplication(AppComponent, {
providers: [provideZonelessChangeDetection()],
});
步骤 3:添加浏览器错误监听器(推荐)
为了更好的错误处理,建议同时添加浏览器全局错误监听器:
typescript
import { provideBrowserGlobalErrorListeners } from "@angular/core";
bootstrapApplication(AppComponent, {
providers: [
provideZonelessChangeDetection(),
provideBrowserGlobalErrorListeners(),
],
});
步骤 4:运行测试并移除警告
运行应用和测试,如果看到 NG0914: using zoneless but still loading Zone.js 警告,说明您遗漏了一个 polyfill。清理后重新运行即可。
无 Zone 模式下的变更检测
仍然会触发变更检测的情况
是的,完全没问题。以下情况仍会触发变更检测:
- 模板事件(如
(click)、(input)等) - Signals 值的变化
- 组件输入(@Input)的变化
不再自动触发的情况
您失去的是"所有异步操作都会触发变更检测"的全局行为。例如:
setTimeout、setInterval不会自动触发- Promise 的
then回调不会自动触发 - 第三方库的异步操作不会自动触发
如果您之前隐式依赖于此,现在需要显式地进行------这对性能和代码清晰度都是有益的。
仍然有效的常见模式
以下模式在无 Zone 模式下依然有效:
Signals 驱动 UI
typescript
// model signal
const count = signal(0);
const double = computed(() => count() * 2);
// event updates the signal
increment() { count.set(count() + 1); }
HTTP + Signals
将 HTTP 请求的结果分配给一个 signal(或 resource),并在模板中绑定它;当 signal 更改时,UI 会自动更新。
markForCheck
调用 markForCheck 会触发应用渲染,因此有时不需要重构为 signals,使用 markForCheck 也能正常工作。
迁移注意事项
在迁移过程中,请注意以下几点:
第三方库兼容性
大多数主要库已支持无 Zone,但如果某些库修补了定时器或直接使用了 Zone API,您可能需要更新或进行小的调整。好消息是:Angular CDK 和 Angular Material 已完全支持无 Zone。
避免隐式依赖
如果您之前依赖于随机的 setTimeout 来"触发"变更检测,请切换到 signals 或分派适当的事件。例如:
typescript
// 不推荐:依赖 setTimeout 触发变更检测
setTimeout(() => {
this.data = newData;
}, 100);
// 推荐:使用 signal 或显式触发
this.dataSignal.set(newData);
// 或
this.data = newData;
this.cdr.markForCheck();
常见问题解答
我需要将所有内容重写为 Signals 吗?
不需要。但您的状态越多地由 signals 驱动,您就越能感受到其优势。已经使用 Inputs 和事件的组件将顺利迁移。您可以逐步迁移,不需要一次性重写所有代码。
SSR/水合(hydration)怎么办?
无 Zone 已通过 SSR 错误处理和刷新时机进行了强化。如果您使用的是 v20+ 的 SSR,迁移到 v21 没有问题。Angular 团队已经确保 SSR 场景下的稳定性。
无 Zone 实际上稳定吗?
是的。v20.2 将其提升为稳定版;v21 使其成为新应用的默认设置。这意味着它已经过充分测试,可以安全地用于生产环境。
总结
Angular v21 中的无 Zone 不仅仅是内部的清理------它是一个新的默认设置,使 Angular 感觉更轻盈、更敏捷、更有目的性。您保留了良好的开发体验(事件、Inputs、Signals),同时减少了运行时开销。如果您一直在犹豫,v21 是您迈出这一步的推动力。
团队行动计划
-
先在开发模式下尝试无 Zone,确认无误后,在生产环境中保持启用。
-
验证第三方库的兼容性,确保所有依赖都能正常工作。
-
全应用范围内推广,并享受更平静、可预测的渲染体验。
祝您升级顺利------并享受 Zone.js 的沉默。🥂