角信号:保持反应性列表
#javascript #网络开发人员 #angular #reactivity

Pawel Kozlowski有一条非常被低估的推文:

在本文中,让我们将该MOBX文章中的示例应用于角信号。

在示例中,我将在包含$符号的信号的前缀变量:$itIsSignal。没有此前缀的变量和字段不是信号。阅读本文后,您可能会同意,它有助于查看应将其称为要读(并观察)的函数。或者您可能决定不使用此公约 - 我不坚持您:)

示例是从MOBX文章示例中转换的,但我将用阅读和跟踪功能取代词语'观看函数(或只是watcher()),因为在角度信号中,需要在观察功能中读取信号才能观察到。

现在,Angular已实现了两个观察者:effect()功能和模板。他们在角信号反应性中实现了相同的角色,因此我将使用watcher()引用两个。

让我们已经开始:

import { signal } from "@angular/core";

type Author = {
  $name: WritableSignal<string>;
  $age?: WritableSignal<number | undefined>;
};

class Message {
  $title: WritableSignal<string>;
  $author: WritableSignal<Author>;
  $likes: WritableSignal<string[]>;

  constructor(title: string, author: string, likes: string[]) {
    this.$title = signal(title);

    this.$author = signal({
      $name: signal(author)
    });

    this.$likes = signal(likes);
  }

  updateTitle(title: string) {
    this.$title.set(title);
  }
}

let message = new Message('Foo', 'Michel', ['Joe', 'Sara']);

正确:阅读观看功能

watcher(() => {
  console.log(message.$title());
});

message.updateTitle('Bar');

在这里,我们将在控制台中收到预期的更新,因为watcher()已读取$title,此后,它将在接收到更新通知时重新阅读此信号。

不正确:更改不可观察的参考

watcher(() => {
  console.log(message.$title());
});

message = new Message('Bar', 'Martijn', ['Felicia', 'Marcus']);

在这里,我们替换了消息,但是watcher()使用了对另一个变量的引用,并且不会通知我们替换了参考。

不正确:在观看功能之外读取信号

const title = message.$title();

watcher(() => {
  console.log(title);
});

message.updateTitle('Bar');

在这里,title不是信号。这是我们在观看功能之外阅读的值,因此在我们更新信号时不会通知watcher()

正确:阅读观看功能

watcher(() => {
  console.log(message.$author().$name());
});

message.$author().$name.set("Sara");

message.$author.set({
  $name: signal("Joe")
});

watcher()函数中,我们读取$author$name。因此,每次我们更新它们中的任何一个时,都会通知watcher()

不正确:在不阅读的情况下存储对观察信号的本地引用

const author = message.$author();

watcher(() => {
  console.log(author.$name());
});

message.$author().$name.set("Sara");

message.$author = signal({ $name: signal("Joe") });

第一个更改将被捡起,message.$author()author是同一对象,并且在watcher()中读取.$name属性。

但是,第二个更改没有得到拾取,因为watcher()并未跟踪watcher()的关系。 watcher()仍在使用旧author

ð普通陷阱:console.log

watcher(() => {
  console.log(message);
});

// Won't trigger a re-run.
message.updateTitle("Hello world");

在上面的示例中,更新的消息标题会被打印,因为它没有在watcher()中读取。 watcher()仅取决于消息变量,这不是信号,而是常规变量。换句话说,$title没有由watcher()读取。

正确:更新对象

watcher(() => {
  console.log(message.$likes().length);
});

message.$likes.mutate(likes => likes.push("Jennifer"));

message.$likes.update(likes => ([...likes, "Jennifer"]));

这将按预期工作:在角信号中,调用mutate()方法将始终导致更新通知。

如果一个角信号包含一个对象,则默认情况下,新值将始终被视为与上一个值不相等(除非您覆盖相等性检查功能)。结果,第二行还将触发更新通知。

不正确:读取信号异步

watcher(() => {
  setTimeout(() => {
    console.log(message.$likes().join(", "));
  }, 10);
});

message.$likes.mutate(likes => likes.push("Jennifer"));

在角信号中,观察功能无法检测异步呼叫中的反应性图依赖性。

结论

利用其他框架和从我们自己的实验中的知识积累的经验,我们可以在无反应性列车中完全释放自动依赖性跟踪的力量。