前些日子我在reddit上看到一个哥们提了一个关于Angular Signal的问题,大意是“我看了那个关于counter的signal的例子,也知道set和computed的一切,但是我就是不明白signal到底是干什么用的”。看到这个问题我会心一笑,因为这完全就是我的想法。在Angular推出了那个绚烂的新网站的那段日子,大街上的每个人都在谈论着signal,仿佛他们早就知道这一切,似乎signal是所有编程语言都有的功能,唯独Angular没有,而现在终于有了,大家弹冠相庆。那段日子我感觉自己被孤立,于是花了很多时间去看各种Angular Signal的文章,而这些文章大部分还是在重复那个counter的例子,如果你已经对那个糟糕的文档感到厌倦,我非常理解你的愤怒。所以我现在要把我了解到的Angular Signal介绍给你,毕竟我们是同道中人,我们都是从counter例子起步,开始寻找正确的方向。
首先让我把真正的困惑拿到桌面上:如果你像我一样关心用户的感受,你一定会有一种意识:some_var 和 some_var() 是两个完全不同的东西,在大多数编程语言中,带有括号意味着这是一个函数,函数意味着它将比一个变量产生更大的开销。所以当你看到有人不用 counter = 0; total = counter + 100;
而是非要用函数去干这个事情的时候,就会觉得很奇怪。
但是以现在Signal的火爆程度,你会从心里意识到这个东西是有意义的,你不得不使用它,否则你的代码会在未来某个时刻被淘汰掉,而事实也正是如此。下面我将介绍都应该在什么时候使用Signal,以及为什么这么做。
规则1:当你的模板中使用了某个组件变量,并且该变量会改变时,你要使用Signal
<p>{{title}}<p>
@if(loading){
<p>Loading, please wait...</p>
} @else {
<p>Items from book {{bookName}}</p>:
@for(item of items; track $index){
<p>{{item.id}} - {{item.name}}</p>
}
}
上面是一个常见的模板,组件加载后会设置一个title,然后使用http get从服务器获取数据,在做这些事情之前设置了一个loading状态,然后获取数据后显示在模版中:
title : string = 'My Book Store';
loading : boolean = false;
bookName : string = '';
items : Items[] = [];
ngOnInit() : void{
this.loadBookById(0);
}
onButtonLoadBook_123() : void{
this.loadBookById(123);
}
onButtonLoadBook_456() : void{
this.loadBookById(456);
}
private loadBookById(id : number) : void{
this.loading = true;
this.http_get(id).subscribe({next : (data) => {
this.bookName = data.bookName;
this.items = data.items;
this.loading = false;
}});
}
这段代码运行的一直很正常,但是你一定看到过这种标题的文章:“xxx tips to xxx your Angular Application Performance in year xxxx”:
其中必有“Tip n: 使用 ChangeDetectionStrategy.OnPush”:
@Component({
selector: 'app-my-component',
changeDetection: ChangeDetectionStrategy.OnPush,
templateUrl: ...
})
我觉得很奇怪:如果OnPush这么好为什么Angular不直接默认使用OnPush?当我把那行ChangeDetectionStrategy
代码加入到@Component里,我就知道为什么了:某些状态的更新了但是模板不去显示新的结果。我想如果你关心最近Angular的发展你应该已经知道了前因后果:Angular正在准备废除掉zone.js,而OnPush更新检测就是代替zone.js的新手段,前提是你需要把所有模版里会在运行时改变的值改用signal: