Angular2依赖库之zone.js

翻译 2016年05月31日 15:23:44

本文翻译自Pascal Precht的《Zones in Angular2》

《Understanding Zones》一文中,我们通过创建profiling zone了解到了Zones的强大之处。我们知道了Zones 是一种执行过程的上下文,它允许我们hook into我们的异步任务。如果你还没读过该文,我们极度建议您能够先看看,因为本文是基于该文的。在本文中,我们将会进一步的了解Zones在Angular 2中扮演着什么样的角色。

Angualr的黄金搭档—Zones

It turns out that, the problem that Zones solve, plays very nicely with what Angular needs in order to perform change detection in our applications. Did you ever ask yourself when and why Angular performs change detection? What is it that tells Angular “Dude, a change probably occurred in my application. Can you please check?”.
Before we dive into these questions, let’s first think about what actually causes this change in our applications. Or rather, what can change state in our applications. Application state change is caused by three things:

  • Events - User events like click, change, input, submit, …
  • XMLHttpRequests - E.g. when fetching data from a remote service
  • Timers - setTimeout(), setInterval(), because JavaScript

It turns out that these three things have something in common. Can you name it? … Correct! They are all asynchronous.

Why do you think is this important? Well … because it turns out that these are the only cases when Angular is actually interested in updating the view. Let’s say we have an Angular 2 component that executes a handler when a button is clicked:

@Component({
  selector: 'my-component',
  template: `
    <h3>We love {{name}}</h3>
    <button (click)="changeName()">Change name</button>
  `
})
class MyComponent {

  name:string = 'thoughtram';

  changeName() {
    this.name = 'Angular';
  }
}

如果你不熟悉(click)语法,你可以去读一读我们的Angular2模板语法揭秘这篇文章。简而言之,通过(click)语法我们可以为<button>元素上的click事件设置事件处理函数。

当我们点击了该组件上的按钮,changeName函数将会执行,该组件的name属性将会被设置为Angular。我们同样希望这个改变能同时反馈到DOM上,Angular将会相应的更新视图绑定数据。

下面的例子我们将会通过使用setTimeout()来更新我们的name属性。请注意,在视图模板中,我们移除了<button>元素。

@Component({
  selector: 'my-component',
  template: `
    <h3>We love {{name}}</h3>
  `
})
class MyComponent implements OnInit {

  name:string = 'thoughtram';

  ngOnInit() {
    setTimeout(() => {
      this.name = 'Angular';
    }, 1000);
  }
}

我们根本不需要写任何额外的代码去告诉Angular框架,一个属性值改变了。不需要写ng-click, $timeout,$scope.$apply()等代码.

If you’ve read our article on understanding Zones, you know that this works obviously because Angular takes advantage of Zones. Zones monkey-patches global asynchronous operations such as setTimeout() and addEventListener(), which is why Angular can easily find out, when to update the DOM.

In fact, the code that tells Angular to perform change detection whenever the VM turn is done, is as simple as this:

ObservableWrapper.subscribe(this.zone.onTurnDone, () => {
  this.zone.run(() => {
    this.tick();
  });
});

tick() {
  // perform change detection
  this.changeDetectorRefs.forEach((detector) => {
    detector.detectChanges();
  });
}

Whenever Angular’s zone emits an onTurnDone event, it runs a task that performs change detection for the entire application. If you’re interested in how change detection in Angular 2 works, watch out, we’re going to publish another article on that soon.

But wait, where does the onTurnDone event emitter come from? This is not part of the default Zone API, right? It turns out that Angular introduces its own zone called NgZone.

NgZone in Angular 2

NgZone is basically a forked zone that extends its API and adds some additional functionality to its execution context. One of the things it adds to the API is the following set of custom events we can subscribe to, as they are observable streams:

  • onTurnStart() - Notifies subscribers just before Angular’s event turn starts. Emits an event once per browser task that is handled by Angular.
  • onTurnDone() - Notifies subscribers immediately after Angular’s zone is done processing the current turn and any micro tasks scheduled from that turn.
  • onEventDone() - Notifies subscribers immediately after the final onTurnDone() callback before ending VM event. Useful for testing to validate application state.

If “Observables” and “Streams” are super new to you, you might want to read our article on Taking advantage of Observables in Angular 2.

The main reason Angular adds its own event emitters instead of relying on beforeTask and afterTask callbacks, is that it has to keep track of timers and other micro tasks. It’s also nice that Observables are used as an API to handle these events.

Running code outside Angular’s zone

Since NgZone is really just a fork of the global zone, Angular has full control over when to run something inside its zone to perform change detection and when not. Why is that useful? Well, it turns out that we don’t always want Angular to magically perform change detection.

As mentioned a couple of times, Zones monkey-patches pretty much any global asynchronous operations by the browser. And since NgZone is just a fork of that zone which notifies the framework to perform change detection when an asynchronous operation has happened, it would also trigger change detection when things like mousemove events fire.

We probably don’t want to perform change detection every time mousemove is fired as it would slow down our application and results in very bad user experience.

That’s why NgZone comes with an API runOutsideAngular() which performs a given task in NgZone’s parent zone, which does not emit an onTurnDone event, hence no change detection is performed. To demonstrate this useful feature, let’s take look at the following code:

@Component({
  selector: 'progress-bar',
  template: `
    <h3>Progress: {{progress}}</h3>
    <button (click)="processWithinAngularZone()">
      Process within Angular zone
    </button>
  `
})
class ProgressBar {

  progress: number = 0;

  constructor(private zone: NgZone) {}

  processWithinAngularZone() {
    this.progress = 0;
    this.increaseProgress(() => console.log('Done!'));
  }
}

Nothing special going on here. We have component that calls processWithinAngularZone() when the button in the template is clicked. However, that method calls increaseProgress(). Let’s take a closer look at this one:

increaseProgress(doneCallback: () => void) {
  this.progress += 1;
  console.log(`Current progress: ${this.progress}%`);

  if (this.progress < 100) {
    window.setTimeout(() => {
      this.increaseProgress(doneCallback);
    }, 10);
  } else {
    doneCallback();
  }
}

increaseProgress() calls itself every 10 milliseconds until progress equals 100. Once it’s done, the given doneCallback will execute. Notice how we use setTimeout() to increase the progress.

Running this code in the browser, basically demonstrates what we already know. After each setTimeout() call, Angular performs change detection and updates the view, which allows us to see how progress is increased every 10 milliseconds. It gets more interesting when we run this code outside Angular’s zone. Let’s add a method that does exactly that.

processOutsideAngularZone() {
  this.progress = 0;
  this.zone.runOutsideAngular(() => {
    this.increaseProgress(() => {
      this.zone.run(() => {
        console.log('Outside Done!');
      });
    });
  });
}

processOutsideAngularZone() also calls increaseProgress() but this time using runOutsideAngularZone() which causes Angular not to be notified after each timeout. We access Angular’s zone by injecting it into our component using the NgZone token.

The UI is not updated as progress increases. However, once increaseProgress() is done, we run another task inside Angular’s zone again using zone.run() which in turn causes Angular to perform change detection which will update the view. In other words, instead of seeing progress increasing, all we see is the final value once it’s done. Check out the running code in action right here.

Zones have now also been proposed as a standard at TC39, maybe another reason to take a closer look at them.

相关文章推荐

angular2 脏检查总述--zone.js 原理

angular2 脏检查总述 这系列文章将介绍angular2的脏值检查是如何工作的?如何比ng1更高效?带着上述问题,让我们一起来看看angular2这禽兽(谁让它叫angular,又那么生猛)干...

依赖注入

在接触angular之前,从来没有接触过依赖注入,

ZONE.js

了解ZONE 除非明确的支出不同 否则所有内容基于Angular version >= 2.x 在NG-Conf2014 Brian 有一个很好的介绍ZONE的视频 PS:请使用正确的...

异步任务管理神器-zone.js

angular2使用了zone.js,这篇文章将介绍一下zone.js是干什么的,以及zone.js在angular2中是怎么应用的。

zone.js - 暴力之美

原文出处:http://www.cnblogs.com/whitewolf/p/zone-js.html 在ng2的开发过程中,Angular团队为我们带来了一个新的库 – zone.js。...

Angular引入第三方库

如果我们想在Angular中使用第三方的库,比如jquery或bootstrap等,该如果做呢?首先我们先来看看package.json这个文件,在目录介绍那篇博客中我们已经知道,package.js...

4.认识Angular组件之2

开源中国博客地址 11. 变化监测:Angular提供了数据绑定的功能.所谓的数据绑定就是将组件类的数据和页面的DOM元素关联起来.当数据发生变化时,Angular能够监测到这些变化,并对其所绑...

Angular2.js

  • 2016-10-16 15:05
  • 1.03MB
  • 下载

Angular2.min.js

  • 2016-10-16 15:01
  • 572KB
  • 下载

七步从Angular.JS菜鸟到专家(2):Scopes

这是"AngularJS - 七步从菜鸟到专家"系列的第二篇。在第一篇我们展示了如何开始搭建一个Angular应用。在这一篇里,我们要讨论一个理解AngularJS运作原理所必须的基本概念,以及你如何...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)