Signal in Angular
Angular 16+ 开始引入了 Signal,按照官方的说法是为了替代 zone。当第一眼看到signal,我曾遐想翩翩,立刻想到了 Vue 中的Ref,又想到了mobx。然而,Angular 官方给的例子居然用了一个count,一个计数的例子。它居然不用对象来举例。我脑补了好多细节。但是当真的尝试却发现,我想多了。那么简单一句话,Angular 里面的 Signal 究竟是个啥,有啥用处? 先给结论
结论
- signal 除了使用的时候多了个方法调用
signaldata()
,除此之外,它跟它里面的对象是同一个对象。没有任何区别。
const orignData = { a: 1, b: { c: 1 } };
const data = signal(orignData);
data() === originData; // true
- signal 对象跟原始对象有什么区别
唯一的区别是当 signal 对象发送变化的时候,这些使用signaldata()
会被 angularmarkForCheck, 注意不是detachChange() - signal 对象中的update() vs set()有啥区别
这两个方法没有任何区别。纯粹是两个不同的用法 - 既然 signal 对象跟原始对象是一样的,那可以直接修改原始对象吗?
你当然可以修改原始对象。如果 signal 中的对象引用没有发生变化,修改也有可能被反应到 UI 上。但是有限制- 对于 OnPush 的组件,如果直接修改对象,不会被 angular 标记,如果使用update/set并且 signal 对象被认为发生改变,则会被标记
- 如何认为 signal 对象发生了变化?
signal(initalvalue:any,options?:{equal:((pre,cur)=>boolean)})
,如果没有提供第二个参数,那么默认是通过===
来判断是否发生改变。所以,在使用update/set方法的时候一定要返回一个新的对象{...obj}
,否则 signal 对象会认为没有发生变化。也就是你跟修改原始对象的效果是一致的。 - 如何看待 computed 对象?
它其实就是一个对于依赖的 signal 的对象的推导(derive)。当最后一次执行 computed 函数体时,用到的 signal 对象发生变化,computed 的值才会被重新计算,否则,不会被计算。 - 那如何看待 signal input 呢。
我认为它就是一个 signal 的桥架语法糖。将 signal 对象的链路通过 input 连起来。如果没有它,你得通过一些不正常的方式来传递 signal 对象。例如,hack 的方式我没有试,也不知道是否合法,但从 js 语法来说是合法的。
// parent
<child [hackSignal]="signal1" [signalInput]="signal2()"></child>
signal1=signal(1);
signal2=signal(2);
// child component
<div>{{hackSignal().xxx}}</div>
<div>{{signalInput().xxx}}</div>
@Input()
hackSignal:WritableSignal<T>();
signalInput=input.required<T>();