为什么angular2会多次执行方法?

gst*_*low 2 lifecycle angular

我的应用程序结构如下所示:

在此处输入图片说明

ts:

...
export class TodoListComponent {

    get sortedTodos():ITodo[] {
            console.log(this.counter++);
            ...
        } 
    ....
Run Code Online (Sandbox Code Playgroud)

的HTML:

  <div class="todo-item" *ngFor="let todo of sortedTodos" [class.completed]="todo.completed">
        <todo-list-item [todo]="todo" class="todo-item" (deleted)="onTodoDeleted(todo)"
                        (toggled)="onTodoUpdated($event)"></todo-list-item>
    </div>
Run Code Online (Sandbox Code Playgroud)

如果我启动应用程序,我会在控制台中看到:

1
2
3
4
5
6
Run Code Online (Sandbox Code Playgroud)

我真的对此行为感到困惑。对我来说,它看起来很奇怪,我认为它可能会导致错误和性能问题。请解释为什么执行6!一次加载页面的次数。

我不确定我是否提供了主题中所有需要的信息。随意提出其他要求。也可以找到所有代码库bitbucket回购链接

聚苯乙烯

完整的ts文件内容:

import {Component, Input, Output, EventEmitter} from "@angular/core"

import {ITodo} from "../../shared/todo.model";
import {TodoService} from "../../shared/todoService";

@Component({
    moduleId: module.id,
    selector: "todo-list",
    templateUrl: "todo-list.component.html",
    styleUrls: ["todo-list.component.css"],
})
export class TodoListComponent {
    @Input() todos:ITodo[];

    @Output() updated:EventEmitter<ITodo> = new EventEmitter<ITodo>();
    @Output() deleted:EventEmitter<ITodo> = new EventEmitter<ITodo>();

    get sortedTodos():ITodo[] {
        return !this.todos ? [] :
            this.todos.map((todo:ITodo)=>todo)
                .sort((a:ITodo, b:ITodo)=> {
                    if (a.title > b.title) {
                        return 1;
                    } else if (a.title < b.title) {
                        return -1;
                    }
                    return 0;
                })
                .sort((a:ITodo, b:ITodo)=> (+a.completed - (+b.completed)));
    }

    onTodoDeleted(todo:ITodo):void {
        this.deleted.emit(todo);
    }

    onTodoUpdated(todo:ITodo):void {
        this.updated.emit(todo);
    }

    constructor(private todoService:TodoService) {
    }
}
Run Code Online (Sandbox Code Playgroud)

yur*_*zui 5

它执行6次,因为:

课堂上有一种tick方法,由2个变化检测周期执行3次。如果通过调用启用生产模式,它将执行3次ApplicationRefenableProdMode()

ApplicationRef是对页面上运行的Angular应用程序的引用。该tick方法从根到叶运行更改检测。

这是tick方法的外观(https://github.com/angular/angular/blob/2.3.0/modules/%40angular/core/src/application_ref.ts#L493-L509):

tick(): void {
  if (this._runningTick) {
    throw new Error('ApplicationRef.tick is called recursively');
  }

  const scope = ApplicationRef_._tickScope();
  try {
    this._runningTick = true;
    this._views.forEach((view) => view.ref.detectChanges()); // check
    if (this._enforceNoNewChanges) {
      this._views.forEach((view) => view.ref.checkNoChanges()); // check only for debug mode
    }
  } finally {
      this._runningTick = false;
      wtfLeave(scope);
  }
}
Run Code Online (Sandbox Code Playgroud)

对于调试模式,将 tick启动两个更改检测周期。因此detectChangesInternal在编译视图内将被调用两次。

在此处输入图片说明

并且由于您的sortedTodos属性是一个吸气剂,因此它将每次作为函数执行。

在此处了解更多信息(Angular 2中的变更检测

这样我们就知道我们的sortedTodos吸气剂被两次叫一次tick

为什么该tick方法执行3次?

1)首先tick是通过引导应用程序手动运行。

private _loadComponent(componentRef: ComponentRef<any>): void {
  this.attachView(componentRef.hostView);
  this.tick();
Run Code Online (Sandbox Code Playgroud)

https://github.com/angular/angular/blob/2.3.0/modules/%40angular/core/src/application_ref.ts#L479

2) Angular2在zonejs中运行,因此它是管理变更检测的主要工具。上面提到的ApplicationRef已订阅zone.onMicrotaskEmpty

this._zone.onMicrotaskEmpty.subscribe(
  {next: () => { this._zone.run(() => { this.tick(); }); }});
Run Code Online (Sandbox Code Playgroud)

https://github.com/angular/angular/blob/2.3.0/modules/%40angular/core/src/application_ref.ts#L433

onMicrotaskEmpty事件是区域 稳定时的指示器

private checkStable() {
  if (this._nesting == 0 && !this._hasPendingMicrotasks && !this._isStable) {
    try {
      this._nesting++;
      this._onMicrotaskEmpty.emit(null); // notice this
    } finally {
      this._nesting--;
      if (!this._hasPendingMicrotasks) {
        try {
          this.runOutsideAngular(() => this._onStable.emit(null));
        } finally {
          this._isStable = true;
        }
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

https://github.com/angular/angular/blob/2.3.0/modules/%40angular/core/src/zone/ng_zone.ts#L195-L211

因此,在执行一些zonejs任务后,将触发此事件

3)您使用的是angular2-in-memory-web-api包,当您尝试获取模拟数据时,它会执行以下操作:

createConnection(req: Request): Connection {
    let res = this.handleRequest(req);

    let response = new Observable<Response>((responseObserver: Observer<Response>) => {
      if (isSuccess(res.status)) {
        responseObserver.next(res);
        responseObserver.complete();
      } else {
        responseObserver.error(res);
      }
      return () => { }; // unsubscribe function
    });

    response = response.delay(this.config.delay || 500); // notice this
    return {
      readyState: ReadyState.Done,
      request: req,
      response
    };
}
Run Code Online (Sandbox Code Playgroud)

https://github.com/angular/in-memory-web-api/blob/0.0.20/src/in-memory-backend.service.ts#L136-L155

它开始定期的zonejs任务周期,该周期使区域成为一个区域,unStable并在发出上述任务后onMicrotaskEmpty再次发出上述事件

您可以在此处找到有关zonejs的更多详细信息

总结

因此,如您所见,您的解决方案有点错误。您不应在模板中使用getter或函数作为绑定。可能的解决方案,您可以在这里找到