开发指南-生命周期钩子 翻译完毕
This commit is contained in:
parent
3e03e0993f
commit
940d0d9c22
@ -641,7 +641,7 @@ table(width="100%")
|
||||
|
||||
[`@ContentChild`](../api/core/ContentChild-var.html) 和
|
||||
[`@ContentChildren`](../api/core/ContentChildren-var.html)属性装饰器允许一个组件查询
|
||||
被从其他地方投射到自己视图里的其他组件的实例。
|
||||
被从其它地方投影进自己视图里的其他组件的实例。
|
||||
|
||||
+makeExample('cb-ts-to-js/ts/app/heroes-queries.component.ts', 'content')(format="." )
|
||||
|
||||
|
@ -469,7 +469,7 @@ a(id="qualify-dependency-lookup")
|
||||
But when this component is projected into a *parent* component, that parent component becomes the host.
|
||||
We look at this second, more interesting case in our next example.
|
||||
|
||||
宿主组件通常是申请这个依赖的组件。但当这个组件被投射(projected)进一个*父组件*后,这个父组件就变成了宿主。我们先思考一下,更多有趣的案例还在后面。
|
||||
宿主组件通常是申请这个依赖的组件。但当这个组件被投影(projected)进一个*父组件*后,这个父组件就变成了宿主。我们先思考一下,更多有趣的案例还在后面。
|
||||
|
||||
### Demonstration
|
||||
### 示范
|
||||
@ -490,14 +490,14 @@ a(id="qualify-dependency-lookup")
|
||||
Angular *projects* (*transcludes*) the corresponding `HeroContactComponent` into the `HeroBioComponent` view,
|
||||
placing it in the `<ng-content>` slot of the `HeroBioComponent` template:
|
||||
|
||||
我们在`<hero-bio>`标签中插入了`<hero-contact>`元素。Angular就会把相应的`HeroContactComponent`*投射*(*transcludes*)进`HeroBioComponent`的视图里,
|
||||
我们在`<hero-bio>`标签中插入了`<hero-contact>`元素。Angular就会把相应的`HeroContactComponent`*投影*(*transcludes*)进`HeroBioComponent`的视图里,
|
||||
将它放在`HeroBioComponent`模板的`<ng-content>`标签槽里。
|
||||
|
||||
+makeExample('cb-dependency-injection/ts/app/hero-bio.component.ts','template','app/hero-bio.component.ts (template)')(format='.')
|
||||
:marked
|
||||
It looks like this, with the hero's telephone number from `HeroContactComponent` projected above the hero description:
|
||||
|
||||
从`HeroContactComponent`获得的英雄电话号码,被投射到上面的英雄描述里,看起来像这样:
|
||||
从`HeroContactComponent`获得的英雄电话号码,被投影到上面的英雄描述里,看起来像这样:
|
||||
|
||||
figure.image-display
|
||||
img(src="/resources/images/cookbooks/dependency-injection/hero-bio-and-content.png" alt="bio and contact")
|
||||
|
@ -144,7 +144,7 @@ figure
|
||||
|
||||
When we need a reference to the `AppComponent`, we **import** it like this:
|
||||
|
||||
当需要引用`AppComponent`时,我们像这样**导入**它:
|
||||
当需要引用`AppComponent`时,我们要像这样**导入**它:
|
||||
|
||||
+makeExample('architecture/ts/app/main.ts', 'import', 'app/main.ts (节选)')(format=".")
|
||||
:marked
|
||||
@ -225,7 +225,7 @@ figure
|
||||
is about all we need to know.
|
||||
|
||||
“模块加载与导入”背后的基础设施是一个很重要的话题,但它不在Angular简介的范围内。
|
||||
只要焦点是我们的应用程序,我们只需要知道*import*和*export*就够了。
|
||||
目前的焦点是我们的应用程序,我们只需要知道*import*和*export*就够了。
|
||||
|
||||
:marked
|
||||
The key take-aways are:
|
||||
@ -260,7 +260,7 @@ figure
|
||||
The shell at the application root with navigation links, that list of heroes, the hero editor ...
|
||||
they're all views controlled by Components.
|
||||
|
||||
**组件**控制屏幕中像补丁那么大的一小块地方,这块地方我们称之为*视图*。
|
||||
**组件**控制屏幕中巴掌大的那么一小块地方,这块地方我们称之为*视图*。
|
||||
应用的“外壳”包括一些导航链接、英雄列表、英雄编辑器…… 它们都是由组件控制的视图。
|
||||
|
||||
We define a Component's application logic - what it does to support the view - inside a class.
|
||||
@ -444,7 +444,7 @@ code-example(language="html").
|
||||
in the space indicated by `<hero-detail>` tags.
|
||||
Angular will do so only if we mention the `HeroDetailComponent` in this `directives` array.
|
||||
|
||||
* `directives` - 一个数组,包含*此*模板需要(依赖的)的组件或指令。
|
||||
* `directives` - 一个数组,包含*此*模板需要依赖的组件或指令。
|
||||
看看模板的最后一行,这表示我们期待Angular把`HeroDetailComponent`的实例放在`<hero-detail>`标签中。
|
||||
但是,只有当我们在`directives`数组中提及到`HeroDetailComponent`的时候,Angular才会这么做。
|
||||
|
||||
@ -453,7 +453,7 @@ code-example(language="html").
|
||||
so it can get the list of heroes to display. We'll get to dependency injection in a moment.
|
||||
|
||||
* `providers` - 一个数组,包含组件所依赖的服务所需要的*依赖注入供应商*。
|
||||
这是告诉Angular该组件的构造函数需要一个`HeroService`服务,这样组件可以从服务获得用来显示英雄列表的数据。
|
||||
这是在告诉Angular:该组件的构造函数需要一个`HeroService`服务,这样组件就可以从服务中获得用来显示英雄列表的那些数据。
|
||||
我们一会儿就讲到了依赖注入。
|
||||
|
||||
figure
|
||||
@ -553,7 +553,7 @@ figure
|
||||
Angular processes *all* data bindings once per JavaScript event cycle,
|
||||
depth-first from the root of the application component tree.
|
||||
|
||||
Angular在每个JavaScript事件周期中一次性处理*所有的*数据绑定,它会从组件树的根部开始,从下往上处理。
|
||||
Angular在每个JavaScript事件周期中一次性处理*所有的*数据绑定,它会从组件树的根部开始,用深度优先的方式处理。
|
||||
|
||||
figure
|
||||
img(src="/resources/images/devguide/architecture/component-databinding.png" alt="数据绑定" style="float:left; width:300px; margin-left:-40px;margin-right:10px" )
|
||||
@ -821,7 +821,7 @@ figure
|
||||
In brief, we must have previously registered a **provider** of the `HeroService` with the `Injector`.
|
||||
A provider is something that can create or return a service, typically the service class itself.
|
||||
|
||||
简单的说,我们必须在(要求注入`HeroService`)之前,注册一个`HeroService`的**供应商Provider**到注入器。
|
||||
简单的说,我们必须在要求注入`HeroService`之前,注册一个`HeroService`的**供应商Provider**到注入器。
|
||||
供应商可以创建并返回服务,通常返回的就是这个“服务类”本身。
|
||||
|
||||
We can register providers at any level of the application component tree.
|
||||
@ -876,7 +876,7 @@ figure
|
||||
|
||||
* we register *providers* with injectors.
|
||||
|
||||
* 我们注册*供应商*到注入器。
|
||||
* 我们把*供应商*注册到注入器。
|
||||
|
||||
<a id="other-stuff"></a>
|
||||
.l-main-section
|
||||
|
@ -110,7 +110,7 @@ table(width="100%")
|
||||
It shows us how to set up the libraries and tools we'll need to write *any* Angular app.
|
||||
|
||||
1. 试用[“快速起步”](../quickstart.html)。“快速起步”是Angular 2世界中的“Hello, World”。
|
||||
它会告诉我们如何安装在*任何*Angular应用程序都会用到的库和工具。
|
||||
它会告诉我们如何准备在*任何*Angular应用程序都会用到的库和工具。
|
||||
|
||||
1. Take the *Tour of Heroes* [Tutorial](../tutorial), which picks up from where the QuickStart leaves off
|
||||
and builds a simple data-driven app.
|
||||
@ -160,7 +160,7 @@ table(width="100%")
|
||||
These snippets are excerpts from a sample application that accompanies the chapter.
|
||||
|
||||
每一章都包含一些能在我们自己的应用中复用的代码片段。
|
||||
这些片段来之于自己那章附带的范例应用。
|
||||
这些片段来自于相应章节附带的范例应用。
|
||||
|
||||
block example-links
|
||||
:marked
|
||||
|
@ -11,31 +11,32 @@ block includes
|
||||
A Component has a lifecycle managed by Angular itself. Angular creates it, renders it, creates and renders its children,
|
||||
checks it when its data-bound properties change, and destroys it before removing it from the DOM.
|
||||
|
||||
一个组件有一个被Angular本身管理的生命周期。Angular创建它,渲染它,创建和渲染它的子级,在它的数据绑定属性变化时检查它,并在它被从DOM移除前销毁它。
|
||||
组件有一个被Angular管理的生命周期。Angular创建它,渲染它,创建并渲染它的子组件,在它被绑定的属性发生变化时检查它,并在它从DOM中被移除前销毁它。
|
||||
|
||||
Angular offers **component lifecycle hooks**
|
||||
that give us visibility into these key moments and the ability to act when they occur.
|
||||
|
||||
Angular提供**组件生命周期钩子**,提高了这些关键时刻的能见度,并给了我们在它们发生的时候采取行动的能力。
|
||||
Angular提供了**组件生命周期钩子**,让我们能看到这些关键时刻,并赋予我们对此采取行动的能力。
|
||||
|
||||
We cover these hooks in this chapter and demonstrate how they work in code.
|
||||
|
||||
我们在本章节覆盖这些钩子,演示它们在代码中是怎么工作的。
|
||||
在本章中,我们将全面讲解这些钩子,演示下在代码层面它们是如何工作的。
|
||||
|
||||
* [The lifecycle hooks](#hooks-overview)
|
||||
|
||||
* [生命周期钩子](#hooks-overview)
|
||||
* [生命周期钩子概览](#hooks-overview)
|
||||
|
||||
* [The hook-call sequence](#hook-sequence)
|
||||
|
||||
* [钩子调用顺序](#hook-sequence)
|
||||
* [钩子的调用顺序](#hook-sequence)
|
||||
|
||||
* [Other Angular lifecycle hooks](#other-lifecycles)
|
||||
|
||||
* [其他Angular生命周期钩子](#other-lifecycles)
|
||||
* [其它Angular生命周期钩子](#other-lifecycles)
|
||||
|
||||
* [The lifecycle sample](#the-sample)
|
||||
|
||||
* [生命周期例子](#the-sample)
|
||||
* [范例](#the-sample)
|
||||
|
||||
* [All](#peek-a-boo)
|
||||
|
||||
@ -55,11 +56,15 @@ block includes
|
||||
|
||||
* [AfterViewInit and AfterViewChecked](#afterview)
|
||||
|
||||
* [AfterViewInit和AfterViewChecked](#afterview)
|
||||
|
||||
* [AfterContentInit and AfterContentChecked](#aftercontent)
|
||||
|
||||
* [AfterContentInit和AfterContentChecked](#aftercontent)
|
||||
|
||||
p Try the #[+liveExampleLink2()].
|
||||
|
||||
p 试一试#[+liveExampleLink2()]。
|
||||
p 试一试#[+liveExampleLink2('在线例子')]。
|
||||
|
||||
a#hooks-overview
|
||||
.l-main-section
|
||||
@ -71,25 +76,25 @@ a#hooks-overview
|
||||
Directive and component instances have a lifecycle
|
||||
as Angular creates, updates, and destroys them.
|
||||
|
||||
在Angular新建、更新和销毁指令和组件的实例时,它们有一个生命周期。
|
||||
当Angular新建、更新和销毁指令和组件的实例时,就形成了一个生命周期。
|
||||
|
||||
Developers can tap into key moments in that lifecycle by implementing
|
||||
one or more of the *Lifecycle Hook* interfaces in the Angular `core` library.
|
||||
|
||||
开发者可以介入这些生命周期的关键时刻,通过实现一个或多个Angular `Core`库里的*生命周期钩子*接口。
|
||||
通过实现一个或多个Angular `core`库里定义的*生命周期钩子*接口,开发者可以介入该生命周期中的这些关键时刻。
|
||||
|
||||
Each interface has a single hook method whose name is the interface name prefixed with `ng`.
|
||||
For example, the `OnInit` interface has a hook method named `ngOnInit`.
|
||||
We might implement it in a component class like this:
|
||||
|
||||
每个接口都有单独一个钩子方法,它们的名字是对应接口名字加上前缀`ng`。比如,`OnInit`接口有一个钩子方法叫做`ngOnInit`。
|
||||
我们可能在一个组件类像这样实现它:
|
||||
每个接口都有唯一的一个钩子方法,它们的名字是由接口名再加上`ng`前缀构成的。比如,`OnInit`接口有一个钩子方法叫做`ngOnInit`。
|
||||
我们可以在一个组件类中实现它,就像这样:
|
||||
+makeExample('lifecycle-hooks/ts/app/peek-a-boo.component.ts', 'ngOnInit', 'peek-a-boo.component.ts (excerpt)')(format='.')
|
||||
:marked
|
||||
No directive or component will implement all of them and some of the hooks only make sense for components.
|
||||
Angular only calls a directive/component hook method *if it is defined*.
|
||||
|
||||
没有指令或者组件实现所有的接口,有些钩子是针对组件的。Angular只有在指令/组件的钩子方法已经被定义时调用它们。
|
||||
没有指令或者组件会实现所有这些接口,而有些钩子只对组件有意义。只有在指令/组件中*定义过的*那些钩子方法才会被Angular调用。
|
||||
block optional-interfaces
|
||||
.l-sub-section
|
||||
:marked
|
||||
@ -101,29 +106,29 @@ block optional-interfaces
|
||||
The JavaScript language doesn't have interfaces.
|
||||
Angular can't see TypeScript interfaces at runtime because they disappear from the transpiled JavaScript.
|
||||
|
||||
从纯粹的技术性方面来看,接口对JavaScript和TypeScript开发者是可选的。JavaScript语言本身没有接口。
|
||||
Angular在运行是看不到TypeScript接口,因为它们在编译为JavaScript的时候消失了。
|
||||
从纯技术的角度讲,接口对JavaScript和TypeScript的开发者都是可选的。JavaScript语言本身没有接口。
|
||||
Angular在运行时看不到TypeScript接口,因为它们在编译为JavaScript的时候已经消失了。
|
||||
|
||||
Fortunately, they aren't necessary.
|
||||
We don't have to add the lifecycle hook interfaces to our directives and components to benefit from the hooks themselves.
|
||||
|
||||
幸运的事,它们不是必须的。我们不需要在指令和组件上面添加生命周期钩子接口来获得钩子带来的好处。
|
||||
幸运的是,它们也不是必须的。我们不需要在指令和组件上添加生命周期钩子接口就能获得钩子带来的好处。
|
||||
|
||||
Angular instead inspects our directive and component classes and calls the hook methods *if they are defined*.
|
||||
Angular will find and call methods like `ngOnInit()`, with or without the interfaces.
|
||||
|
||||
取而代之,Angular检测指令和组件类,并在钩子方法已经被定义时调用它们。
|
||||
Agnular会找到并调用像`ngOnInit()`的钩子方法,有没有接口并不影响。
|
||||
Angular会去检测我们的指令和组件类,一旦发现钩子方法被定义了,就调用它们。
|
||||
Agnular会找到并调用像`ngOnInit()`这样的钩子方法,有没有接口无所谓。
|
||||
|
||||
Nonetheless, we strongly recommend adding interfaces to TypeScript directive classes
|
||||
in order to benefit from strong typing and editor tooling.
|
||||
|
||||
虽然如此,我们强烈推荐在TypeScript指令类里添加接口,来获得强类型和编辑工具辅助等好处。
|
||||
虽然如此,我们还是强烈建议你在TypeScript指令类中添加接口,以获得强类型和IDE等编辑器带来的好处。
|
||||
|
||||
:marked
|
||||
Here are the component lifecycle hook methods:
|
||||
|
||||
这里是组件生命周期钩子方法列表:
|
||||
这里是组件生命周期钩子的方法列表:
|
||||
|
||||
### Directives and Components
|
||||
|
||||
@ -133,15 +138,19 @@ table(width="100%")
|
||||
col(width="20%")
|
||||
col(width="80%")
|
||||
tr
|
||||
th Hook
|
||||
th Purpose
|
||||
th
|
||||
p Hook
|
||||
p 钩子
|
||||
th
|
||||
p Purpose
|
||||
p 用途
|
||||
tr(style=top)
|
||||
td ngOnInit
|
||||
td
|
||||
:marked
|
||||
Initialize the directive/component after Angular initializes the data-bound input properties.
|
||||
|
||||
在Angular初始化数据绑定输入属性后,用来初始化指令或组件。
|
||||
当Angular初始化完数据绑定的输入属性后,用来初始化指令或组件。
|
||||
tr(style=top)
|
||||
td ngOnChanges
|
||||
td
|
||||
@ -149,7 +158,7 @@ table(width="100%")
|
||||
Respond after Angular sets a data-bound input property.
|
||||
The method receives a `changes` object of current and previous values.
|
||||
|
||||
在Angular设置一个数据绑定输入属性后作出反应。这个方法接受一个包含了当前和之前的值的`changes`对象。
|
||||
当Angular设置了一个被绑定的输入属性后触发。该回调方法会收到一个包含当前值和原值的`changes`对象。
|
||||
tr(style=top)
|
||||
td ngDoCheck
|
||||
td
|
||||
@ -157,7 +166,7 @@ table(width="100%")
|
||||
Detect and act upon changes that Angular can or won't
|
||||
detect on its own. Called every change detection run.
|
||||
|
||||
用来监测Angular自己可以或者不会检测的变化,并作出相应行动。贼每个变化监测运行时被调用。
|
||||
用来监测所有变化(无论是Angular本身能检测的还是无法检测的),并作出相应行动。在每次执行“变更检测”时被调用。
|
||||
tr(style=top)
|
||||
td ngOnDestroy
|
||||
td
|
||||
@ -165,7 +174,7 @@ table(width="100%")
|
||||
Cleanup just before Angular destroys the directive/component.
|
||||
Unsubscribe observables and detach event handlers to avoid memory leaks.
|
||||
|
||||
在Angular销毁指令或组件之前做一些清洁工作,不如退订监测和分离时间处理器,以防内存泄露。
|
||||
在Angular销毁指令或组件之前做一些清理工作,比如退订可观察对象和移除事件处理器,以免导致内存泄露。
|
||||
|
||||
:marked
|
||||
### Components only
|
||||
@ -176,114 +185,122 @@ table(width="100%")
|
||||
col(width="20%")
|
||||
col(width="80%")
|
||||
tr
|
||||
th Hook
|
||||
th Purpose
|
||||
th
|
||||
p Hook
|
||||
p 钩子
|
||||
th
|
||||
p Purpose
|
||||
p 用途
|
||||
tr(style=top)
|
||||
td ngAfterContentInit
|
||||
td
|
||||
:marked
|
||||
After Angular projects external content into its view.
|
||||
|
||||
在Angular投射外来内容到自己的视图后调用。
|
||||
当Angular把外来内容投影进自己的视图之后调用。
|
||||
tr(style=top)
|
||||
td ngAfterContentChecked
|
||||
td
|
||||
:marked
|
||||
After Angular checks the bindings of the external content that it projected into its view.#
|
||||
|
||||
在Angular检查它投射到自己视图的外部内容的绑定后调用。
|
||||
当Angular检查完那些投影到自己视图中的外来内容的数据绑定之后调用。
|
||||
tr(style=top)
|
||||
td ngAfterViewInit
|
||||
td
|
||||
:marked
|
||||
After Angular creates the component's view(s).
|
||||
|
||||
在Angular新建组件的视图后调用。
|
||||
在Angular创建完组件的视图后调用。
|
||||
tr(style=top)
|
||||
td ngAfterViewChecked
|
||||
td
|
||||
:marked
|
||||
After Angular checks the bindings of the component's view(s).
|
||||
|
||||
在Angular检查组件视图的绑定后调用。
|
||||
在Angular检查完组件视图中的绑定后调用。
|
||||
:marked
|
||||
Angular does not call the hook methods in this order.
|
||||
|
||||
Angular不会按照上面的顺序调用这些钩子方法。
|
||||
Angular并不会按照表中所列的顺序调用这些钩子方法。
|
||||
|
||||
a(id="hook-sequence")
|
||||
.l-main-section
|
||||
:marked
|
||||
## Lifecycle sequence
|
||||
|
||||
## 生命周期顺序
|
||||
## 生命周期的顺序
|
||||
|
||||
*After* Angular creates a component/directive by `new`-ing its constructor,
|
||||
it calls the lifecycle hook methods in the following sequence at specific moments:
|
||||
|
||||
Angular使用构造函数新建一个组件或指令后,它在特定的时刻按照下面的顺序调用生命周期钩子方法@
|
||||
当Angular使用构造函数新建一个组件或指令后,就会按下面的顺序在特定时刻调用这些生命周期钩子方法:
|
||||
table(width="100%")
|
||||
col(width="20%")
|
||||
col(width="80%")
|
||||
tr
|
||||
th Hook
|
||||
th Timing
|
||||
th
|
||||
p Hook
|
||||
p 钩子
|
||||
th
|
||||
p Timing
|
||||
p 调用时机
|
||||
tr(style=top)
|
||||
td ngOnChanges
|
||||
td
|
||||
:marked
|
||||
before `ngOnInit` and when a data-bound input property value changes.
|
||||
|
||||
当数据绑定输入属性的值变化时,在`ngOnInit`之前调用。
|
||||
当被绑定的输入属性的值发生变化时调用,首次调用一定会发生在`ngOnInit`之前。
|
||||
tr(style=top)
|
||||
td ngOnInit
|
||||
td
|
||||
:marked
|
||||
after the first `ngOnChanges`.
|
||||
|
||||
在第一个`ngOnChanges`时候调用。
|
||||
在首次`ngOnChanges`之后调用。
|
||||
tr(style=top)
|
||||
td ngDoCheck
|
||||
td
|
||||
:marked
|
||||
during every Angular change detection cycle.
|
||||
|
||||
在每个Angular变化监测周期中调用。
|
||||
在每个Angular变更检测周期中调用。
|
||||
tr(style=top)
|
||||
td ngAfterContentInit
|
||||
td
|
||||
:marked
|
||||
after projecting content into the component.
|
||||
|
||||
投射内容到组件后调用。
|
||||
当把内容投影进组件之后调用。
|
||||
tr(style=top)
|
||||
td ngAfterContentChecked
|
||||
td
|
||||
:marked
|
||||
after every check of projected component content.
|
||||
|
||||
每次检查被投射的组件内容之后调用。
|
||||
每次完成被投影组件内容的变更检测之后调用。
|
||||
tr(style=top)
|
||||
td ngAfterViewInit
|
||||
td
|
||||
:marked
|
||||
after initializing the component's views and child views.
|
||||
|
||||
初始化组件视图和子视图之后调用。
|
||||
初始化完组件视图及其子视图之后调用。
|
||||
tr(style=top)
|
||||
td ngAfterViewChecked
|
||||
td
|
||||
:marked
|
||||
after every check of the component's views and child views.
|
||||
|
||||
每次检查组件视图和子视图之后调用。
|
||||
每次做完组件视图和子视图的变更检测之后调用。
|
||||
tr(style=top)
|
||||
td ngOnDestroy
|
||||
td
|
||||
:marked
|
||||
just before Angular destroys the directive/component.
|
||||
|
||||
在Angular销毁指令和组件之前调用。
|
||||
当Angular每次销毁指令/组件之前调用。
|
||||
|
||||
a(id="other-lifecycles")
|
||||
.l-main-section
|
||||
@ -296,36 +313,35 @@ a(id="other-lifecycles")
|
||||
The router, for instance, also has it's own [router lifecycle hooks](router.html#router-lifecycle-hooks)
|
||||
that allow us to tap into specific moments in route navigation.
|
||||
|
||||
其他Angular子系统除了有我们已经列出来的组件钩子外,可能有它们自己的生命周期钩子。比如路由器,它有自己的[路由器生命周期钩子](router.html#router-lifecycle-hooks),
|
||||
为我们提供了插入到特定的路由浏览时刻的能力。
|
||||
Angular的其它子系统除了有我们已经列出来的这些组件钩子外,还可能有它们自己的生命周期钩子。比如路由器,它就有自己的[路由器生命周期钩子](router.html#router-lifecycle-hooks),
|
||||
为我们提供了在路由导航中的特定时机进行介入的能力。
|
||||
|
||||
A parallel can be drawn between `ngOnInit` and `routerOnActivate`.
|
||||
Both are prefixed so as to avoid collision, and both run right when a component is 'booting' up.
|
||||
|
||||
一对平衡线可以在`ngOnInit`和`routerOnActivate`之间被划出来。两种都有前缀用来防止冲突,两者都是在一个组件刚刚“启动”时被调用。
|
||||
`ngOnInit`和`routerOnActivate`是并行执行的。它们都用前缀来防止冲突,而且都是在一个组件刚刚“启动”时被调用的。
|
||||
|
||||
3rd party libraries might implement their hooks as well in order to give us, the developers, more
|
||||
control over how these libraries are used.
|
||||
|
||||
第三方库也可能实现它们自己的钩子,用来给我们开发者提供更多如何使用它们的控制。
|
||||
第三方库也可能会实现它们自己的钩子,以便让我们这些开发者在使用时能做更多的控制。
|
||||
|
||||
a#the-sample
|
||||
.l-main-section
|
||||
h2 Lifecycle exercises
|
||||
|
||||
h2 生命周期练习
|
||||
p.
|
||||
The #[+liveExampleLink()]
|
||||
demonstrates the lifecycle hooks in action through a series of exercises
|
||||
presented as components under the control of the root `AppComponent`.
|
||||
|
||||
#[+liveExampleLink()] 通过一些在根组件`AppComponent`控制下的组件的练习,演示了生命周期钩子的运作。
|
||||
p #[+liveExampleLink('运行在线范例')] 通过在受控于根组件`AppComponent`的一些组件上进行的一系列练习,演示了生命周期钩子的运作方式。
|
||||
|
||||
:marked
|
||||
They follow a common pattern: a *parent* component serves as a test rig for
|
||||
a *child* component that illustrates one or more of the lifecycle hook methods.
|
||||
|
||||
它们遵循了一个常用的模式:一个*子级*组件演示一个或多个生命周期钩子方法,一个*父级*组件被当作该*子级*组件的测试台。
|
||||
它们遵循了一个常用的模式:用*子组件*演示一个或多个生命周期钩子方法,而*父组件*被当作该*子组件*的测试台。
|
||||
|
||||
Here's a brief description of each exercise:
|
||||
|
||||
@ -335,8 +351,12 @@ table(width="100%")
|
||||
col(width="20%")
|
||||
col(width="80%")
|
||||
tr
|
||||
th Component 组件
|
||||
th Description 描述
|
||||
th
|
||||
p Component
|
||||
p 组件
|
||||
th
|
||||
p Description
|
||||
p 描述
|
||||
tr(style=top)
|
||||
td <a href="#peek-a-boo">Peek-a-boo</a>
|
||||
td
|
||||
@ -344,7 +364,7 @@ table(width="100%")
|
||||
Demonstrates every lifecycle hook.
|
||||
Each hook method writes to the on-screen log.
|
||||
|
||||
展示每个生命周期钩子,每个钩子方法在屏幕上显示日志。
|
||||
展示每个生命周期钩子,每个钩子方法都会在屏幕上显示一条日志。
|
||||
tr(style=top)
|
||||
td <a href="#spy">Spy</a>
|
||||
td
|
||||
@ -353,12 +373,12 @@ table(width="100%")
|
||||
We create a `SpyDirective` that logs when the element it spies upon is
|
||||
created or destroyed using the `ngOnInit` and `ngOnDestroy` hooks.
|
||||
|
||||
指令也有生命周期钩子。我们新建一个`SpyDirective`,利用`ngOnInit`和`ngOnDestroy`钩子,在每个被监视的元素被新建或销毁时输出日志。
|
||||
指令也同样有生命周期钩子。我们新建了一个`SpyDirective`,利用`ngOnInit`和`ngOnDestroy`钩子,在它所监视的每个元素被创建或销毁时输出日志。
|
||||
|
||||
We apply the `SpyDirective` to a `<div>` in an `ngFor` *hero* repeater
|
||||
managed by the parent `SpyComponent`.
|
||||
|
||||
我们把`SpyDirective`应用到一个在父级组件里的`ngFor`*英雄*重复器的`<div>`里面。
|
||||
我们把`SpyDirective`应用到父组件里的`ngFor`*英雄*重复器(repeater)的`<div>`里面。
|
||||
tr(style=top)
|
||||
td <a href="#onchanges">OnChanges</a>
|
||||
td
|
||||
@ -367,19 +387,26 @@ table(width="100%")
|
||||
every time one of the component input properties changes.
|
||||
Shows how to interpret the `changes` object.
|
||||
|
||||
|
||||
这里将会看到:每当组件的输入属性发生变化时,Angular会如何以`changes`对象作为参数去调用`ngOnChanges`钩子。
|
||||
展示了该如何理解和使用`changes`对象。
|
||||
tr(style=top)
|
||||
td <a href="#docheck">DoCheck</a>
|
||||
td
|
||||
:marked
|
||||
Implements an `ngDoCheck` method with custom change detection.
|
||||
See how often Angular calls this hook and watch it post changes to a log.
|
||||
|
||||
实现了一个`ngDoCheck`方法,通过它可以自定义变更检测逻辑。
|
||||
这里将会看到:Angular会用什么频度调用这个钩子,监视它的变化,并把这些变化输出成一条日志。
|
||||
tr(style=top)
|
||||
td <a href="#afterview">AfterView</a>
|
||||
td
|
||||
:marked
|
||||
Shows what Angular means by a *view*.
|
||||
Demonstrates the `ngAfterViewInit` and `ngAfterViewChecked` hooks.
|
||||
|
||||
显示Angular中的*视图*所指的是什么。
|
||||
演示了`ngAfterViewInit`和`ngAfterViewChecked`钩子。
|
||||
tr(style=top)
|
||||
td <a href="#aftercontent">AfterContent</a>
|
||||
td
|
||||
@ -387,31 +414,51 @@ table(width="100%")
|
||||
Shows how to project external content into a component and
|
||||
how to distinguish projected content from a component's view children.
|
||||
Demonstrates the `ngAfterContentInit` and `ngAfterContentChecked` hooks.
|
||||
|
||||
展示如何把外部内容投影进组件中,以及如何区分“投影进来的内容”和“组件的子视图”。
|
||||
演示了`ngAfterContentInit`和`ngAfterContentChecked`钩子。
|
||||
tr(style=top)
|
||||
td Counter
|
||||
td
|
||||
p Counter
|
||||
p 计数器
|
||||
td
|
||||
:marked
|
||||
Demonstrates a combination of a component and a directive
|
||||
each with its own hooks.
|
||||
|
||||
演示了组件和指令的组合,它们各自有自己的钩子。
|
||||
|
||||
In this example, a `CounterComponent` logs a change (via `ngOnChanges`)
|
||||
every time the parent component increments its input counter property.
|
||||
Meanwhile, we apply the `SpyDirective` from the previous example
|
||||
to the `CounterComponent` log and watch log entries be created and destroyed.
|
||||
|
||||
在这个例子中,每当父组件递增它的输入属性`counter`时,`CounterComponent`就会通过`ngOnChanges`记录一条变更。
|
||||
同时,我们还把前一个例子中的`SpyDirective`用在`CounterComponent`上,来提供日志,可以同时观察到日志的创建和销毁过程。
|
||||
|
||||
:marked
|
||||
We discuss the exercises in further detail over this chapter as we learn more about the lifecycle hooks.
|
||||
|
||||
接下来,我们将详细讨论这些练习,以学习更多关于生命周期钩子的知识。
|
||||
|
||||
a(id="peek-a-boo")
|
||||
.l-main-section
|
||||
:marked
|
||||
## Peek-a-boo: all hooks
|
||||
## Peek-a-boo:全部钩子
|
||||
The `PeekABooComponent` demonstrates all of the hooks in one component.
|
||||
|
||||
`PeekABooComponent`组件演示了组件中所有可能存在的钩子。
|
||||
|
||||
In real life, we'd rarely if ever implement all of the interfaces like this.
|
||||
We do so in peek-a-boo in order to watch Angular call the hooks in the expected order.
|
||||
|
||||
现实中,我们几乎永远不会像这里一样实现所有这些接口。
|
||||
我们之所以在peek-a-boo中这么做,只是为了观看Angular是如何按照期望的顺序调用这些钩子的。
|
||||
|
||||
In this snapshot, we clicked the *Create...* button and then the *Destroy...* button.
|
||||
|
||||
在下面这个快照中,我们先点击了*Create...*按钮,然后点击了*Destroy...*按钮。
|
||||
figure.image-display
|
||||
img(src="/resources/images/devguide/lifecycle-hooks/peek-a-boo.png" alt="Peek-a-boo")
|
||||
:marked
|
||||
@ -419,57 +466,88 @@ figure.image-display
|
||||
`OnChanges`, `OnInit`, `DoCheck` (3x), `AfterContentInit`, `AfterContentChecked` (3x),
|
||||
`AfterViewInit`, `AfterViewChecked` (3x), and `OnDestroy`.
|
||||
|
||||
日志信息的日志和所规定的钩子调用顺序是一致的:
|
||||
`OnChanges`、`OnInit`、`DoCheck` (3x)、`AfterContentInit`、`AfterContentChecked` (3x)、
|
||||
`AfterViewInit`、`AfterViewChecked` (3x)和`OnDestroy`
|
||||
.l-sub-section
|
||||
:marked
|
||||
The constructor isn't an Angular hook *per se*.
|
||||
We log in it to confirm that input properties (the `name` property in this case) have no assigned values at construction.
|
||||
|
||||
构造函数本质上不应该算作Angular的钩子。
|
||||
我们把它记录在这里只是为了确认在创建期间那些输入属性(这里是`name`属性)没有被赋值。
|
||||
:marked
|
||||
Had we clicked the *Update Hero* button, we'd have seen another `OnChanges` and two more triplets of
|
||||
`DoCheck`, `AfterContentChecked` and `AfterViewChecked`.
|
||||
Clearly these three hooks fire a *lot* and we must keep the logic we put in these hooks
|
||||
as lean as possible!
|
||||
|
||||
如果我们点击*Update Hero*按钮,就会看到另一个`OnChanges`和至少两组`DoCheck`、`AfterContentChecked`和`AfterViewChecked`钩子。
|
||||
显然,这三种钩子被触发了*很多次*,所以我们必须让这三种钩子里的逻辑尽可能的精简!
|
||||
|
||||
Our next examples focus on hook details.
|
||||
|
||||
我们的下一个例子就聚焦于这些钩子的细节上。
|
||||
|
||||
.a(id="spy")
|
||||
.l-main-section
|
||||
:marked
|
||||
## Spying *OnInit* and *OnDestroy*
|
||||
## 窥探*OnInit*和*OnDestroy*
|
||||
|
||||
We're going undercover for these two hooks. We want to know when an element is initialized or destroyed,
|
||||
but we don't want *it* to know we're watching.
|
||||
|
||||
我们将揭开这两个钩子的奥秘。我们想知道一个元素是什么时候被初始化或销毁的,但我们不希望*它*知道我们正在监视它。
|
||||
|
||||
This is the perfect infiltration job for a directive.
|
||||
Our heroes will never know it's there.
|
||||
|
||||
指令是一种完美的渗透方式,我们的英雄永远不会知道该指令的存在。
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
Kidding aside, we're emphasizing two key points:
|
||||
|
||||
不开玩笑了,我们要强调的是两点:
|
||||
|
||||
1. Angular calls hook methods for *directives* as well as components.
|
||||
|
||||
1. 就像对组件一样,Angular也会对*指令*调用这些钩子方法。
|
||||
|
||||
2. A spy directive can gives us insight into a DOM object that we cannot change directly.
|
||||
Obviously we can't change the implementation of a native `div`.
|
||||
We can't modify a third party component either.
|
||||
But we can watch both with a directive.
|
||||
|
||||
|
||||
2. 一个侦探(spy)指令可以让我们在无法直接修改DOM对象实现代码的情况下,透视其内部细节。
|
||||
显然,我们不能修改一个原生`div`元素的实现代码。
|
||||
我们同样不能修改第三方组件。
|
||||
但我们用一个指令就能监视它们了。
|
||||
|
||||
:marked
|
||||
Our sneaky spy directive is simple, consisting almost entirely of `ngOnInit` and `ngOnDestroy` hooks
|
||||
that log messages to the parent via an injected `LoggerService`.
|
||||
|
||||
我们这个鬼鬼祟祟的侦探指令很简单,几乎完全由`ngOnInit`和`ngOnDestroy`钩子组成,它通过一个注入进来的`LoggerService`来把消息记录到父组件中去。
|
||||
|
||||
+makeExample('lifecycle-hooks/ts/app/spy.directive.ts', 'spy-directive')(format=".")
|
||||
|
||||
:marked
|
||||
We can apply the spy to any native or component element and it'll be initialized and destroyed
|
||||
at the same time as that element.
|
||||
Here we attach it to the repeated hero `<div>`
|
||||
|
||||
我们可以把这个侦探指令写到任何原生元素或组件元素上,它将与所在的组件同时初始化和销毁。
|
||||
这里我们把它附加到用来重复显示英雄数据的这个`<div>`上。
|
||||
+makeExample('lifecycle-hooks/ts/app/spy.component.html', 'template')(format=".")
|
||||
|
||||
:marked
|
||||
Each spy's birth and death marks the birth and death of the attached hero `<div>`
|
||||
with an entry in the *Hook Log* as we see here:
|
||||
|
||||
每个“侦探”的出生和死亡也同时标记出了存放英雄的那个`<div>`的出生和死亡。*钩子记录*中的结构看起来是这样的:
|
||||
|
||||
figure.image-display
|
||||
img(src='/resources/images/devguide/lifecycle-hooks/spy-directive.gif' alt="Spy Directive")
|
||||
|
||||
@ -477,51 +555,90 @@ figure.image-display
|
||||
Adding a hero results in a new hero `<div>`. The spy's `ngOnInit` logs that event.
|
||||
We see a new entry for each hero.
|
||||
|
||||
添加一个英雄就会产生一个新的英雄`<div>`。侦探的`ngOnInit`记录下了这个事件。
|
||||
我们看到每添加一个英雄都产生了一条新的日志结构。
|
||||
|
||||
The *Reset* button clears the `heroes` list.
|
||||
Angular removes all hero divs from the DOM and destroys their spy directives at the same time.
|
||||
The spy's `ngOnDestroy` method reports its last moments.
|
||||
|
||||
*Reset*按钮清除了这个`heroes`列表。
|
||||
Angular从DOM中移除了所有英雄的div,并且同时销毁了附加在这些div上的侦探指令。
|
||||
侦探的`ngOnDestroy`方法汇报了它自己的临终时刻。
|
||||
|
||||
The `ngOnInit` and `ngOnDestroy` methods have more vital roles to play in real applications.
|
||||
Let's see why we need them.
|
||||
|
||||
在真实的应用程序中,`ngOnInit`和`ngOnDestroy`方法扮演着更重要的角色。
|
||||
来看看我们为什么需要它们。
|
||||
|
||||
### OnInit
|
||||
|
||||
We turn to `ngOnInit` for two main reasons:
|
||||
|
||||
我们会因为两个主要理由而求助于`ngOnInit`:
|
||||
1. To perform complex initializations shortly after construction
|
||||
1. 在构造函数之后马上执行复杂的初始化逻辑
|
||||
1. To set up the component after Angular sets the input properties
|
||||
1. 在Angular设置完输入属性之后,对该组件进行准备。
|
||||
|
||||
An `ngOnInit` often fetches data for the component as shown in the
|
||||
[Tutorial](../tutorial/toh-pt4.html#oninit) and [HTTP](server-communication.html#oninit) chapters.
|
||||
|
||||
`ngOnInit`通常用来为组件获取数据,就像在[教程](../tutorial/toh-pt4.html#oninit)和[HTTP](server-communication.html#oninit)中所展示的那样。
|
||||
|
||||
We don't fetch data in a component constructor. Why?
|
||||
Because experienced developers agree that components should be cheap and safe to construct.
|
||||
We shouldn't worry that a new component will try to contact a remote server when
|
||||
created under test or before we decide to display it.
|
||||
Constructors should do no more than set the initial local variables to simple values.
|
||||
|
||||
我们没有在组件的构造函数中获取数据。为什么呢?
|
||||
因为有经验的开发者都同意,组件应该能被“便宜”而且安全的被构造出来。
|
||||
在测试环境下新建组件时或在我们决定显示它之前,我们不应该担心它会尝试联系远程服务器。
|
||||
构造函数中除了使用简单的值对局部变量进行初始化之外,什么都不应该做。
|
||||
|
||||
When a component must start working _soon_ after creation,
|
||||
we can count on Angular to call the `ngOnInit` method to jumpstart it.
|
||||
That's where the heavy initialization logic belongs.
|
||||
|
||||
如果组件必须在创建之后_很快_就开始工作,我们可以等Angular调用`ngOnInit`方法来启动它。
|
||||
该方法中才是放重量级初始化逻辑的地方。
|
||||
|
||||
Remember also that a directive's data-bound input properties are not set until _after construction_.
|
||||
That's a problem if we need to initialize the directive based on those properties.
|
||||
They'll have been set when our `ngOninit` runs.
|
||||
|
||||
另外还要记住,在指令的_构造函数完成之前_,那些被绑定的输入属性还都没有值。
|
||||
如果我们需要基于这些属性的值来初始化这个指令,这种情况就会出问题。
|
||||
而当`ngOnInit`执行的时候,这些属性都已经被正确的赋值过了。
|
||||
.l-sub-section
|
||||
:marked
|
||||
Our first opportunity to access those properties is the `ngOnChanges` method which
|
||||
Angular calls before `ngOnit`. But Angular calls `ngOnChanges` many times after that.
|
||||
It only calls `ngOnit` once.
|
||||
|
||||
我们访问这些属性的第一次机会,实际上是`ngOnChanges`方法,Angular会在`ngOnInit`之前调用它。
|
||||
但是在那之后,Angular还会调用`ngOnChanges`很多次。而`ngOnInit`只会被调用一次。
|
||||
:marked
|
||||
### OnDestroy
|
||||
|
||||
Put cleanup logic in `ngOnDestroy`, the logic that *must* run before Angular destroys the directive.
|
||||
|
||||
把清理逻辑放在`ngOnDestroy`中,该逻辑*必须*在Angular销毁这个指令之前运行。
|
||||
|
||||
This is the time to notify another part of the application that this component is going away.
|
||||
|
||||
这是在该组件消失之前,可用来通知应用程序中其它部分的最后一个时间点。
|
||||
|
||||
This is the place to free resources that won't be garbage collected automatically.
|
||||
Unsubscribe from observables and DOM events. Stop interval timers.
|
||||
Unregister all callbacks that this directive registered with global or application services.
|
||||
We risk memory leaks if we neglect to do so.
|
||||
|
||||
这里是用来释放那些不会被垃圾收集器自动回收的各类资源的地方。
|
||||
取消那些对可观察对象和DOM事件的订阅。停止定时器。注销该指令曾注册到全局服务或应用级服务中的各种回调函数。
|
||||
如果我们不这么做,就会有导致内存泄露的风险。
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
@ -530,21 +647,35 @@ figure.image-display
|
||||
We monitor the `OnChanges` hook in this example.
|
||||
Angular calls its `ngOnChanges` method whenever it detects changes to ***input properties*** of the component (or directive).
|
||||
|
||||
在这个例子中,我们监听了`OnChanges`钩子。
|
||||
一旦检测到该组件(或指令)的***输入属性***发生了变化,Angular就会调用它的`ngOnChanges`方法。
|
||||
|
||||
Here is our implementation of the hook.
|
||||
|
||||
这里是我们对此钩子的实现。
|
||||
+makeExample('lifecycle-hooks/ts/app/on-changes.component.ts', 'ng-on-changes', 'OnChangesComponent (ngOnChanges)')(format=".")
|
||||
:marked
|
||||
The `ngOnChanges` method takes an object that maps each changed property name to a
|
||||
[SimpleChange](../api/core/SimpleChange-class.html) object with the current and previous property values.
|
||||
We iterate over the changed properties and log them.
|
||||
|
||||
`ngOnChanges`方法获取了一个对象,它把每个变化了的属性名都映射到了一个[SimpleChange](../api/core/SimpleChange-class.html)对象,
|
||||
该对象中有属性的当前值和前一个值。我们在这些发生了变化的属性上进行迭代,并记录它们。
|
||||
|
||||
The input properties for our example `OnChangesComponent` are `hero` and `power`.
|
||||
|
||||
我们这个例子中`OnChangesComponent`组件的输入属性是`hero`和`power`。
|
||||
+makeExample('lifecycle-hooks/ts/app/on-changes.component.ts', 'inputs')(format=".")
|
||||
:marked
|
||||
The parent binds to them like this:
|
||||
|
||||
父组件中绑定了它们,就像这样:
|
||||
|
||||
+makeExample('lifecycle-hooks/ts/app/on-changes-parent.component.html', 'on-changes')
|
||||
:marked
|
||||
Here's the sample in action as we make changes.
|
||||
|
||||
下面是此例子中的当我们做出更改时的动态效果:
|
||||
|
||||
figure.image-display
|
||||
img(src='/resources/images/devguide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges")
|
||||
@ -553,90 +684,146 @@ figure.image-display
|
||||
We see log entries as the string value of the *power* property changes. But the `ngOnChanges` did not catch changes to `hero.name`
|
||||
That's surprising at first.
|
||||
|
||||
当*power*属性的字符串值变化时,我们看到了相应的日志。但是`ngOnChanges`并没有捕捉到`hero.name`的变化。
|
||||
这是第一个意外。
|
||||
|
||||
Angular only calls the hook when the value of the input property changes.
|
||||
The value of the `hero` property is the *reference to the hero object*.
|
||||
Angular doesn't care that the hero's own `name` property changed.
|
||||
The hero object *reference* didn't change so, from Angular's perspective, there is no change to report!
|
||||
|
||||
Angular只会在输入属性的值变化时调用这个钩子。
|
||||
而`hero`属性的值是一个*到英雄对象的引用*。
|
||||
Angular不会关注这个英雄对象的`name`属性的变化。
|
||||
这个英雄对象的*引用*没有发生变化,于是从Angular的视角看来,也就没有什么需要报告的变化了。
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
## DoCheck
|
||||
We can use the `DoCheck` hook to detect and act upon changes that Angular doesn't catch on its own.
|
||||
|
||||
我们可以使用`DoCheck`钩子来检测那些Angular自身无法捕获的变更并采取行动。
|
||||
.l-sub-section
|
||||
:marked
|
||||
With this method we can detect a change that Angular overlooked.
|
||||
What we do with that information to refresh the display is a separate matter.
|
||||
|
||||
用这个方法我们可以检测到那些被Angular忽略的更改。
|
||||
至于如何用此信息来刷新显示就是另一个问题了。
|
||||
:marked
|
||||
The *DoCheck* sample extends the *OnChanges* sample with this implementation of `DoCheck`:
|
||||
|
||||
*DoCheck*范例通过下面的`DoCheck`实现扩展了*OnChanges*范例:
|
||||
+makeExample('lifecycle-hooks/ts/app/do-check.component.ts', 'ng-do-check', 'DoCheckComponent (ngDoCheck)')(format=".")
|
||||
:marked
|
||||
We manually check everything that we care about, capturing and comparing against previous values.
|
||||
We write a special message to the log when there are no substantive changes
|
||||
to the hero or the power so we can keep an eye on the method's performance characteristics.
|
||||
|
||||
我们手动检测了自己关心的一切,捕获当前值并与以前的值进行比较。
|
||||
当英雄或他的超能力发生了非实质性改变时,我们就往日志中写一条特殊的消息,以便看到该方法特有的表演。
|
||||
|
||||
The results are illuminating:
|
||||
|
||||
其结果就像下面展示的这样:
|
||||
figure.image-display
|
||||
img(src='/resources/images/devguide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck")
|
||||
:marked
|
||||
We now are able to detect when the hero's `name` has changed. But we must be careful.
|
||||
|
||||
我们现在可以监测到英雄的`name`什么时候发生了变化。但我们必须小心。
|
||||
|
||||
The `ngDoCheck` hook is called with enormous frequency —
|
||||
after _every_ change detection cycle no matter where the change occurred.
|
||||
It's called over twenty times in this example before the user can do anything.
|
||||
|
||||
`ngDoCheck`钩子被非常频繁的调用 —— 在_每次_变更检测周期之后,发生了变化的每个地方都会调它。
|
||||
在这个例子中,用户还没有做任何操作之前,它就被调用了超过二十次。
|
||||
|
||||
Most of these initial checks are triggered by Angular's first rendering of *unrelated data elsewhere on the page*.
|
||||
Mere mousing into another input box triggers a call.
|
||||
Relatively few calls reveal actual changes to pertinent data.
|
||||
Clearly our implementation must be very lightweight or the user experience may suffer.
|
||||
|
||||
大部分检查的第一次调用都是在Angular首次渲染该页面中*其它不相关数据*时触发的。
|
||||
仅仅把鼠标移到其它输入框中就会触发一次调用。
|
||||
只有相对较少的调用才是由于对相关数据的修改而触发的。
|
||||
显然,我们的实现必须非常轻量级,否则将损害用户体验。
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
We also see that the `ngOnChanges` method is called in contradiction of the
|
||||
[incorrect API documentation](../api/core/DoCheck-interface.html).
|
||||
|
||||
我们还看到,`ngOnChanges`方法的调用方式与[API文档](../api/core/DoCheck-interface.html)中是不一样的,这是因为API文档过时了。
|
||||
(译注:这是经过与官方开发组沟通得到的消息,由于代码快速迭代,因此API文档现在的更新不够及时,将来会进行一次系统的梳理和更正)
|
||||
.l-main-section
|
||||
:marked
|
||||
## AfterView
|
||||
The *AfterView* sample explores the `AfterViewInit` and `AfterViewChecked` hooks that Angular calls
|
||||
*after* it creates a component's child views.
|
||||
|
||||
*AfterView*例子展示了`AfterViewInit`和`AfterViewChecked`钩子,Angular会在每次创建了组件的子视图后调用它们。
|
||||
|
||||
Here's a child view that displays a hero's name in an input box:
|
||||
|
||||
下面是一个子视图,它用来把英雄的名字显示在一个输入框中:
|
||||
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'child-view', 'ChildComponent')(format=".")
|
||||
:marked
|
||||
The `AfterViewComponent` displays this child view *within its template*:
|
||||
|
||||
`AfterViewComponent`把这个子视图显示*在它的模板中*:
|
||||
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'template', 'AfterViewComponent (template)')(format=".")
|
||||
:marked
|
||||
The following hooks take action based on changing values *within the child view*
|
||||
which we can only reach by querying for the child view via the property decorated with
|
||||
[@ViewChild](../api/core/ViewChild-var.html).
|
||||
|
||||
下列钩子基于*子视图中*的每一次数据变更采取行动,我们只能通过带[@ViewChild](../api/core/ViewChild-var.html)装饰器的属性来访问子视图。
|
||||
|
||||
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'hooks', 'AfterViewComponent (class excerpts)')(format=".")
|
||||
.a(id="wait-a-tick")
|
||||
:marked
|
||||
### Abide by the unidirectional data flow rule
|
||||
### 遵循单向数据流规则
|
||||
The `doSomething` method updates the screen when the hero name exceeds 10 characters.
|
||||
|
||||
当英雄的名字超过10个字符时,`doSomething`方法就会更新屏幕。
|
||||
|
||||
+makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'do-something', 'AfterViewComponent (doSomething)')(format=".")
|
||||
:marked
|
||||
Why does the `doSomething` method wait a tick before updating `comment`?
|
||||
|
||||
为什么在更新`comment`属性之前,`doSomething`方法要等上一拍(tick)?
|
||||
|
||||
Because we must adhere to Angular's unidirectional data flow rule which says that
|
||||
we may not update the view *after* it has been composed.
|
||||
Both hooks fire after the component's view has been composed.
|
||||
|
||||
因为我们必须遵守Angular的“单向数据流”规则,这条规则是说我们不能在一个视图已经被组合好*之后*再更新视图。
|
||||
而这两个钩子都是在组件的视图已经被组合好之后触发的。
|
||||
|
||||
Angular throws an error if we update component's data-bound `comment` property immediately (try it!).
|
||||
|
||||
如果我们立即更新组件中被绑定的`comment`属性,Angular就会抛出一个错误(试试!)。
|
||||
block tick-methods
|
||||
:marked
|
||||
The `LoggerService.tick` methods, which are implemented by a call to `setTimeout`, postpone the update one turn of the of the browser's JavaScript cycle ... and that's long enough.
|
||||
|
||||
`LoggerService.tick`方法,是通过调用`setTimeout`的方式实现的 —— 如果想推迟到浏览器的下一轮JavaScript周期中执行,这样就够了。
|
||||
|
||||
:marked
|
||||
Here's *AfterView* in action
|
||||
|
||||
这里是*AfterView*的操作演示:
|
||||
figure.image-display
|
||||
img(src='/resources/images/devguide/lifecycle-hooks/after-view-anim.gif' alt="AfterView")
|
||||
:marked
|
||||
Notice that Angular frequently calls `AfterViewChecked`, often when there are no changes of interest.
|
||||
Write lean hook methods to avoid performance problems.
|
||||
|
||||
注意,Angular会频繁的调用`AfterViewChecked`,甚至在并没有需要关注的更改时也会触发。
|
||||
所以务必把这个钩子方法写得尽可能精简,以免出现性能问题。
|
||||
|
||||
.l-main-section
|
||||
:marked
|
||||
@ -644,32 +831,52 @@ figure.image-display
|
||||
The *AfterContent* sample explores the `AfterContentInit` and `AfterContentChecked` hooks that Angular calls
|
||||
*after* Angular projects external content into the component.
|
||||
|
||||
*AfterContent*例子展示了`AfterContentInit`和`AfterContentChecked`钩子,Angular会在外来内容被投影到组件中*之后*调用它们。
|
||||
|
||||
### Content projection
|
||||
### 内容投影
|
||||
*Content projection* is a way to import HTML content from outside the component and insert that content
|
||||
into the component's template in a designated spot.
|
||||
|
||||
*内容投影*是从组件外部导入HTML内容,并把它插入在组件模板中指定位置上的一种途径。
|
||||
|
||||
.l-sub-section
|
||||
:marked
|
||||
Angular 1 developers know this technique as *transclusion*.
|
||||
|
||||
Angular 1的开发者大概知道一项叫做*transclusion*的技术,对,这就是它的马甲。
|
||||
|
||||
:marked
|
||||
We'll illustrate with a variation on the [previous](#afterview) example
|
||||
whose behavior and output is almost the same.
|
||||
|
||||
在[前一个](#afterview)例子中,我们解说过此功能的一个变体,它的行为和输出与这个例子完全相同。
|
||||
|
||||
This time, instead of including the child view within the template, we'll import it from
|
||||
the `AfterContentComponent`'s parent. Here's the parent's template.
|
||||
|
||||
这次,我们不再通过模板来把子视图包含进来,而是改从`AfterContentComponent`的父组件中导入它。下面是父组件的模板。
|
||||
+makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'parent-template', 'AfterContentParentComponent (template excerpt)')(format=".")
|
||||
:marked
|
||||
Notice that the `<my-child>` tag is tucked between the `<after-content>` tags.
|
||||
We never put content between a component's element tags *unless we intend to project that content
|
||||
into the component*.
|
||||
|
||||
注意,`<my-child>`标签被包含在`<after-content>`标签中。
|
||||
永远不要在组件标签的内部放任何内容 —— *除非我们想把这些内容投影进这个组件中*。
|
||||
|
||||
Now look at the component's template:
|
||||
|
||||
现在来看下`<after-content>`组件的模板:
|
||||
+makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'template', 'AfterContentComponent (template)')(format=".")
|
||||
:marked
|
||||
The `<ng-content>` tag is a *placeholder* for the external content.
|
||||
They tell Angular where to insert that content.
|
||||
In this case, the projected content is the `<my-child>` from the parent.
|
||||
|
||||
`<ng-content>`标签是外来内容的*占位符*。
|
||||
它告诉Angular在哪里插入这些外来内容。
|
||||
在这里,被投影进去的内容就是来自父组件的`<my-child>`标签。
|
||||
figure.image-display
|
||||
img(src='/resources/images/devguide/lifecycle-hooks/projected-child-view.png' width="230" alt="Projected Content")
|
||||
:marked
|
||||
@ -677,29 +884,49 @@ figure.image-display
|
||||
:marked
|
||||
The tell-tale signs of *content projection* are (a) HTML between component element tags
|
||||
and (b) the presence of `<ng-content>` tags in the component's template.
|
||||
|
||||
下列迹象表明存在着*内容投影*:(a) 在组件的元素标签中有HTML;(b) 组件的模板中出现了`<ng-content>`标签。
|
||||
:marked
|
||||
### AfterContent hooks
|
||||
### AfterContent钩子
|
||||
*AfterContent* hooks are similar to the *AfterView* hooks. The key difference is the kind of child component
|
||||
that we're looking for.
|
||||
|
||||
*AfterContent*钩子和*AfterView*相似。关键的不同点是子组件的类型不同。
|
||||
|
||||
* The *AfterView* hooks concern `ViewChildren`, the child components whose element tags
|
||||
appear *within* the component's template.
|
||||
|
||||
* *AfterView*钩子所关心的是`ViewChildren`,这些子组件的元素标签会出现在该组件的模板*中*。
|
||||
|
||||
* The *AfterContent* hooks concern `ContentChildren`, the child components that Angular
|
||||
projected into the component.
|
||||
|
||||
* *AfterContent*钩子所关心的是`ContentChildren`,这些子组件被Angular投影进该组件中。
|
||||
|
||||
The following *AfterContent* hooks take action based on changing values in a *content child*
|
||||
which we can only reach by querying for it via the property decorated with
|
||||
[@ContentChild](../api/core/ContentChild-var.html).
|
||||
|
||||
下列*AfterContent*钩子基于*子级内容*中值的变化而采取相应的行动,这里我们只能通过
|
||||
带有[@ContentChild](../api/core/ContentChild-var.html)装饰器的属性来查询到“子级内容”。
|
||||
|
||||
+makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'hooks', 'AfterContentComponent (class excerpts)')(format=".")
|
||||
|
||||
:marked
|
||||
### No unidirectional flow worries
|
||||
### 这里不用担心单向数据流规则
|
||||
|
||||
This component's `doSomething` method update's the component's data-bound `comment` property immediately.
|
||||
There's no [need to wait](#wait-a-tick).
|
||||
|
||||
该组件的`doSomething`方法立即更新了组件被绑定的`comment`属性。
|
||||
它[不用等](#wait-a-tick)下一拍。
|
||||
|
||||
Recall that Angular calls both *AfterContent* hooks before calling either of the *AfterView* hooks.
|
||||
Angular completes composition of the projected content *before* finishing the composition of this component's view.
|
||||
We still have a window of opportunity to modify that view.
|
||||
|
||||
回忆一下,Angular在每次调用*AfterView*钩子之前也会同时调用*AfterContent*。
|
||||
Angular在完成当前组件的视图合成之前,就已经完成了被投影内容的合成。
|
||||
所以我们仍然有机会去修改那个视图。
|
||||
|
@ -315,7 +315,7 @@ h2#fetch-data 通过#[b HeroService]获取数据
|
||||
described in the appendix below.
|
||||
Alternatively, we can (temporarily) target a JSON file by changing the endpoint URL:
|
||||
|
||||
一旦我们按附录中所描述的那样配置好了[内存Web API](in-mem-web-api),它*将*返回英雄列表。
|
||||
一旦我们按附录中所描述的那样准备好了[内存Web API](in-mem-web-api),它*将*返回英雄列表。
|
||||
但目前,我们只能(临时性的)使用一个JSON文件来代替这个“内存Web API”。只要修改下服务器的URL就行了:
|
||||
+makeExample('server-communication/ts/app/toh/hero.service.ts', 'endpoint-json')(format=".")
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user