为什么 angular2 多次执行方法?

2023-12-07

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

enter image description here

ts:

...
export class TodoListComponent {

    get sortedTodos():ITodo[] {
            console.log(this.counter++);
            ...
        } 
    ....

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>

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

1
2
3
4
5
6

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

我不确定我是否提供了主题中所有需要的信息。请随意要求其他东西。还可以找到所有代码库Bitbucket 仓库链接

P.S.

完整的 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) {
    }
}

它执行了 6 次,因为:

有一个tick中的方法ApplicationRef类,它是2 个变化检测周期执行 3 次。如果您将通过调用启用生产模式enableProdMode()将会被执行3次

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);
  }
}

For 调试模式 tick starts 两个变化检测周期. So detectChangesInternal在编译视图中将被调用两次。

enter image description here

并且作为你的sortedTodosproperty 是一个 getter,因此每次都会作为函数执行。

在这里阅读更多相关信息(Angular 2 中的变化检测)

那么我们就知道我们的sortedTodosgetter 被调用两次tick

为什么是tick方法执行了3次?

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

private _loadComponent(componentRef: ComponentRef<any>): void {
  this.attachView(componentRef.hostView);
  this.tick();

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(); }); }});

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

onMicrotaskEmpty事件是一个指示器,当zone 变得稳定

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;
        }
      }
    }
  }
}

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
    };
}

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

它启动常规的 zonejs 任务周期,这使得 zoneunStable最后在上面描述的任务执行之后onMicrotaskEmpty再次发生事件

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

  • http://blog.kwintenp.com/how-the-hell-do-zones-really-work/
  • http://blog.thoughtram.io/angular/2016/02/01/zones-in-angular-2.html

Recap:

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

  • 为什么在使用函数时总是执行 Angular 2 中的 *ngIf ?
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 angular2 多次执行方法? 的相关文章

随机推荐

  • 状态栏问题

    正如您从我的图像中看到的 状态栏显示在我的表格视图的顶部 我不明白我做错了什么导致这种情况发生 我确信这将是一个简单的修复 但我只是想念它 任何帮助将不胜感激 谢谢 您可以通过在 ViewDidLoad 方法中编写以下代码来避免此问题 fl
  • 序列化/反序列化不同的属性名称?

    我有一个旧系统 在请求信息调用中返回 xml 其名称如下所示 邮政编码字段 名字字段 然后 同一系统有一个修改调用 它采用如下所示的 xml 邮政编码 名字 姓氏 有没有办法构建一个对象来反序列化请求 同时使用不同的名称序列化 xml 输出
  • 将 Linq-to-Sql 查询的 WHERE 子句作为参数传递

    这可能有点突破了 Linq to Sql 的界限 但考虑到到目前为止它的多功能性 我想我应该问一下 我有 3 个查询 它们选择相同的信息 仅在where子句 现在我知道我可以传递一个委托 但这只允许我过滤已经返回的结果 但我想通过参数构建查
  • $lookup 中其他连接条件的性能严重下降(使用管道)

    因此 在一些代码审查期间 我决定通过改进一个聚合来提高现有查询性能 如下所示 aggregate difference starts here lookup from sessions localField id foreignField
  • Android 10 中的 Android 开发者无 IMEI

    由于 Android 非常重视安全性 并试图让新的 Android 版本更加安全 因此开发人员很难跟上新的安全功能并找到旧方法的替代方案来使他们的应用程序与旧功能兼容 这个问题是关于新Android 10中的IMEI 旧方法通过使用以下代码
  • 创建在 Rust 中实现特征的对象向量

    用Java来说 我试图创建一个对象 严格实例 的集合 向量 每个对象都实现一个接口 特征 因此我可以迭代该集合并对所有对象调用一个方法 我已将其缩减为下面的一个示例文件 其中包含我希望能够更轻松地获得答案的所有部分 main rs try
  • 如何通过API检索Google Doc上文档的所有建议接受内容

    正如标题 我设法通过 API 检索 Google Docs 上所有建议接受的内容 我已经提到过它的指导方针 and 几个帖子但在这个平台上却是徒劳的 下面是我目前拥有的片段 请指教 function myFunction var docum
  • 使用 scipy.interpolate 进行样条表示:低振幅、快速振荡函数的插值效果较差

    我需要 以数字方式 计算我尝试使用两者的函数的一阶和二阶导数splrep and UnivariateSpline创建样条曲线以对函数进行插值以获取导数 然而 对于大小为 10 1 或更低的函数 样条表示本身似乎存在固有问题and正在 快速
  • 当使用 WinSCP 选择要上传到 FTP 服务器的文件的掩码与文件不匹配时,SSIS 任务失败

    我正在创建一个 SSIS 包 它使用脚本任务 通过 WinSCP 将文件上传到 FTP 上传工作正常 但是如果没有上传文件 我需要该过程失败 目前它只是循环并成功通过 在 WinSCP 命令文件中编写脚本方面还没有获得任何帮助 option
  • 如何在selenium中查找具有多个类的元素

    我有一个包含 3 个类的元素 我需要用 selenium 找到它
  • 文本转语音(TTS)-Android

    我是android平台的新手 现在我正在努力TTS Text to Speech 如果我在文本区域中输入文本 并且希望在单击 发言 按钮时将其转换为语音 谁能帮我吗 文本转语音功能内置于 Android 1 6 中 这是一个如何执行此操作的
  • 如何获取 TextView 的精确位置(以像素为单位)?

    我有一部 2560 x 1440 像素的智能手机 现在我正在使用这个功能TextView int locationOnScreen new int 2 txtAp GetLocationInWindow locationOnScreen 它
  • 为具有圆角的 UIImageView 创建阴影?

    我正在尝试创建一个ImageView它有圆角和阴影以赋予它一些深度 我能够为UIImageView 但是每当我添加代码以使其具有圆角时 它只有圆角而没有阴影 我有一个IBOutlet named myImage 并且它位于viewDidLo
  • pandas 中的单列

    有没有办法在不影响数据框其余部分的情况下对 pandas 中的单个列进行舍入 gt gt gt print df item value1 value2 0 a 1 12 1 3 1 a 1 50 2 5 2 a 0 10 0 0 3 b 3
  • 在哪里以及如何定义应用程序属性? - JHIpster

    在 Spring Boot 中 可以在 application properties 文件中定义应用程序属性 例如 Rest 的前缀可以定义为 spring data rest basePath api 对于基于 Spring Boot 的
  • 游标如何引用已删除的行?

    当我从 Android 中的 sqlite 数据库查询返回游标时 它似乎包含固定数量的行 无论数据库发生什么情况 这些行都不会改变 例如 如果在光标打开时删除了某些行 我仍然可以引用已删除的行 这很好 但是它是如何工作的呢 一种猜测可能是
  • 哪些 Boost 功能与 C++11 重叠?

    几年前 我把我的 C 技能束之高阁 现在看来 当我再次需要它们时 情况已经发生了变化 我们现在有了 C 11 我的理解是它重叠了许多 Boost 功能 是否有一些总结 这些重叠在哪里 哪些 Boost 库将成为遗产 建议使用哪些 C 11
  • 将 xml 列中的数据插入到临时表中

    我有一个 xml 列 看起来像 SET XMLData
  • ggplot 在 X 轴下方添加跟踪颜色

    我想在 x 轴下方添加一条线 其颜色取决于未绘制的因素 在此示例中 我正在创建一个箱形图 并希望添加一条指示另一个变量的线 以汽车数据集为例 然后实际思考我正在尝试做的事情 ggplot mtcars aes factor cyl mpg
  • 为什么 angular2 多次执行方法?

    我的应用程序结构如下所示 ts export class TodoListComponent get sortedTodos ITodo console log this counter html div class todo item d