我正在尝试使用 RXJS 制作一个简单的 TODO 应用程序。我有一个包含 TODO 任务的 JSON 服务器模拟数据库。
所以我最终得到了这个 TasksService:
@Injectable({
providedIn: 'root'
})
export class TasksService
{
private _tasks : ITask[] = [];
private _tasks$: BehaviorSubject<ITask[]> = new BehaviorSubject<ITask[]>([]);
constructor (private _http: HttpClient) { }
public getTasks()
{
this.getTasksObservableFromDb().pipe(
tap(
(tasks) =>
{
this._tasks = tasks;
this._tasks$.next(tasks);
}
)
).subscribe();
return this._tasks$;
}
public addTask(task: ITask)
{
this._tasks.push(task);
this._tasks$.next(this._tasks);
}
private getTasksObservableFromDb(): Observable<any>
{
return this._http.get<any>('http://127.0.0.1:3000/tasks');
}
当我添加任务时,我不想立即将它们发布到服务器。 因此,当我从服务器获取任务时,我将它们保存到 _tasks 属性,然后将它们传递给我的 _tasks$:BehaviorSubject 的 next() 方法。 因为后来我想将我的任务批量发布到服务器,现在我只想让它们在 Angular 中正确显示。
在我的 AppComponent 中,我获取任务并将它们分配给我的任务属性。
export class AppComponent implements OnInit
{
public tasks!:BehaviorSubject<ITask[]>;
constructor (private _tasksService: TasksService)
{}
ngOnInit(): void
{
console.log('OnInit');
this.tasks = this._tasksService.getTasks();
}
public addTask()
{
this._tasksService.addTask(
{
id: crypto.randomUUID(),
isImportant: true,
text: 'Added task'
}
);
}
}
在我的 HTML 模板中,我使用异步管道作为我的任务属性并显示我的任务:
<ng-container *ngFor="let task of tasks | async">
{{task.text}}
{{task.id}}
</ng-container>
<button type="button" (click)="addTask()">Add Task</button>
但后来我不小心删除了我的 TaskService 中的这一行:
this._tasks$.next(this._tasks);
所以我的方法现在看起来像这样:
public addTask(task: ITask)
{
this._tasks.push(task);
}
但是添加任务仍然有效!即使我没有为我的BehaviorSubject 传递新任务数组,Angular 也会显示新添加的任务。
所以我决定记录我的任务中的值! :我的 AppComponent 类中的BehaviorSubject<ITask[]> 属性:
public addTask()
{
this._tasksService.addTask(
{
id: crypto.randomUUID(),
isImportant: true,
text: 'Added task'
}
);
this.tasks.pipe(tap((value) => console.log(value)
)).subscribe();
}
并且任务会按预期添加 - 每次获取包含一个任务的数组时:
Array(3) [ {…}, {…}, {…} ] <- Add task button is clicked
Array(4) [ {…}, {…}, {…}, {…} ] <- Add task button is clicked
Array(5) [ {…}, {…}, {…}, {…}, {…} ] <- Add task button is clicked
但是当我将这一行返回到 TaskService 中的 addTask 方法时:
this._tasks$.next(this._tasks);
我得到这些日志:
Array(3) [ {…}, {…}, {…} ] <- Add task button is clicked -> one task is added
Array(4) [ {…}, {…}, {…}, {…} ] <- Add task button is clicked -> one task is added
Array(4) [ {…}, {…}, {…}, {…} ] <- I get the same array
Array(5) [ {…}, {…}, {…}, {…}, {…} ] <- Add task button is clicked -> one task is added
Array(5) [ {…}, {…}, {…}, {…}, {…} ] <- I get the same array
Array(5) [ {…}, {…}, {…}, {…}, {…} ] <- I get the same array once again
所以我有点迷失为什么可观察的行为是这样的......也许我不完全理解 next() 方法?
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号
据我了解,这段代码有两个问题。首先,您不知道为什么控制台日志有重复。其次,即使您没有对行为主题调用“.next()”,您也不知道为什么视图会更新。
让我们从第一个开始。
您需要了解 rxjs observables 和 BehaviourSubjects 是如何工作的。 普通的可观察对象,当您订阅它时,将等待发出某个值,然后每次发生时,它都会调用您附加到它的操作。例如:
现在请注意,在这段代码中,我们仅在 ngOnInit 中订阅了一次。尽管如此,每次调用emitValue()方法(例如从按钮)时,console.log都会被调用。这是因为订阅会一直持续到取消订阅为止。这意味着,每次对主题调用 next() 时都会调用该操作。
那么当您订阅多次时会发生什么?让我们尝试一下:
我们订阅了一个主题 3 次,现在每当发出值时,控制台日志都会被调用 3 次。您创建的每个订阅都会调用它。
现在,当我们查看您的示例时,每次单击 addTask 按钮时都会添加订阅。这就是为什么每次添加任务时,都会多一个控制台日志。 但是,嘿,在您的第一个示例中,当您删除 .next() 时,您有一些控制台日志,即使您没有发出任何值,这是为什么呢?现在我们来到BehaviourSubject。这是一种特殊的主题,它拥有其价值,并在订阅后立即发出它。而且每次你调用 .next() 时它也会发出,但你没有这样做,所以这就是为什么它每次订阅时都会调用 1 个控制台日志。
你应该做的是打电话
仅一次,例如在 ngOnInit() 中
好了,现在我们进入第二期
这非常简单,但需要一些关于引用如何工作的知识。
在您的服务中,您将整个任务数组放入 BehaviourSubject 中。事实上,这个主题持有这个数组的引用。这意味着,每当您将新值推送到数组中时,BehaviourSubject 也会拥有它。这就是发生的情况,每当您调用 addTask 方法时,都会将新值推送到任务数组,并且您的 BehaviourSubject 也有它。