ng-zorro-antd 22.0.0 已正式发布。这个版本最值得关注的变化不是新增了多少视觉组件,而是所有组件统一采用 OnPush 变更检测策略,同时加入日期适配器支持,并扩展上传组件的自定义操作能力。对于表格、表单、日期和上传功能密集的企业级中后台项目,这些变化会直接影响渲染性能、数据更新方式以及组件封装边界。
全面采用 OnPush,性能收益伴随数据流约束
Angular 默认变更检测会在许多异步事件发生后检查组件树。OnPush 则收紧了检查条件,通常在输入引用发生变化、组件内部事件触发、显式请求检测或模板消费的响应式数据更新时重新渲染。
当 ng-zorro-antd 的所有组件都采用 OnPush 后,大型页面有机会减少无效检查。包含多个表格、树、下拉框和复杂表单的页面尤其可能受益。但升级后也要重点排查“数据已经变了,界面却没有更新”的问题。常见原因是业务代码直接修改数组或对象,没有创建新引用。
下面是一个可以直接改造进项目的 Angular 示例。关键点是使用不可变更新,而不是对原数组执行 push:
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { NzTableModule } from 'ng-zorro-antd/table';
interface UserRow {
id: number;
name: string;
enabled: boolean;
}
@Component({
selector: 'app-user-table',
standalone: true,
imports: [NzTableModule],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<button type="button" (click)="addUser()">新增用户</button>
<nz-table #table [nzData]="users">
<thead>
<tr>
<th>ID</th>
<th>姓名</th>
<th>状态</th>
</tr>
</thead>
<tbody>
@for (user of table.data; track user.id) {
<tr>
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{{ user.enabled ? '启用' : '停用' }}</td>
</tr>
}
</tbody>
</nz-table>
`
})
export class UserTableComponent {
users: UserRow[] = [
{ id: 1, name: 'Alice', enabled: true }
];
addUser(): void {
const id = this.users.length + 1;
// 创建新数组引用,确保 OnPush 子组件能够观察到输入变化。
this.users = [
...this.users,
{ id, name: `User ${id}`, enabled: true }
];
}
}
升级检查时,应特别搜索 push、splice、对象属性原地赋值,以及直接修改表格、树节点和选项数组的代码。并非所有原地修改都会立即出错,因为事件或其他检测机制可能碰巧触发刷新,但这种代码会让行为依赖调用上下文,难以稳定测试。
可以先用以下命令定位高风险写法,再逐处判断是否需要改为不可变更新:
rg '\.(push|splice|sort|reverse)\(' src/app
rg 'this\.[A-Za-z0-9_]+\[[^]]+\]\s*=' src/app
npm test
npm run build
日期适配器让日期模型不再被组件绑死
22.0.0 新增日期适配器支持。它的重要性在于,日期组件与业务日期模型之间可以拥有更明确的转换层。企业系统经常同时面对本地时区、UTC、纯日期字段、后端 ISO 字符串以及不同日期库;如果转换逻辑散落在页面组件中,很容易出现偏移一天、重复格式化或序列化结果不一致的问题。
来源摘要没有给出适配器的具体注册 API,因此不应直接假设某个类名或 Provider 配置。可以先按下面的边界整理业务代码,再依据 22.0.0 的正式类型定义接入真实适配器。以下是一个可运行的纯 TypeScript 日期边界示例,不依赖特定日期库:
export function parseDateOnly(value: string): Date {
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
if (!match) {
throw new Error(`Invalid date-only value: ${value}`);
}
const [, year, month, day] = match;
return new Date(Number(year), Number(month) - 1, Number(day));
}
export function formatDateOnly(value: Date): string {
const year = value.getFullYear();
const month = String(value.getMonth() + 1).padStart(2, '0');
const day = String(value.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
这里刻意使用本地时间构造“纯日期”,避免把生日、账期等不带时区的字段误当作 UTC 时间戳。真正接入日期适配器前,需要先确定后端契约:字段究竟是日期、当地时间还是绝对时间点。适配器可以统一转换,但不能替代清晰的数据语义。
上传自定义操作适合承载业务动作
本次更新还提到上传组件支持自定义操作。企业应用中的文件列表通常不只有删除和下载,还可能包括预览、重试、查看扫描结果、补充元数据、设置主附件或发起审批。
接入这类能力时,建议把“操作展示”与“上传状态机”分开。组件负责渲染按钮和派发事件,业务服务负责权限检查、API 调用、错误恢复和审计记录。不要仅根据前端隐藏按钮来实现权限控制,服务端仍需验证当前用户是否有权操作对应文件。
由于摘要没有展开 22.0.0 自定义操作的模板参数和事件签名,迁移时应以安装包中的类型声明和版本文档为准。可以先升级依赖并检查编译器给出的准确接口:
npm install ng-zorro-antd@22.0.0
npm run build
npm test
对于大型仓库,建议单独建立一个上传组件演示页,覆盖上传中、成功、失败、无权限和文件被服务端删除等状态,再将验证过的封装推广到业务页面。
升级时不要只看编译是否通过
这次升级的核心检查项可以压缩为四条:
- 确认 Angular、TypeScript 与其他框架依赖满足 22.0.0 的版本要求,以包内 peer dependencies 为准。
- 排查传给 ng-zorro 组件的数组和对象是否存在原地修改,优先改成不可变更新或 Signal、Observable 驱动的数据流。
- 为日期字段明确“纯日期、本地时间、UTC 时间点”三种语义,再评估日期适配器的接入方式。
- 为上传自定义操作补齐权限、失败重试、加载状态和服务端校验测试。
ng-zorro-antd 22.0.0 的价值不只在于组件能力增加。全面采用 OnPush 会推动应用代码建立更可预测的数据流,而日期适配器与上传自定义操作则给复杂业务留下了更清晰的扩展位置。升级可以从一个高频表格页和一个上传页开始,通过性能采样与回归测试确认收益,再逐步扩大范围。