文章翻译自:https://toddmotto.com/angular-1-5-lifecycle-hooks
讨论能够访问issue:https://github.com/Jocs/jocs.github.io/issues/3javascript
生命周期钩子是一些简单的函数,这些函数会在Angular应用组件特定生命周期被调用。生命周期钩子在Angular 1.5版本被引入,一般与.component()方法一块儿使用,并在接下来的几个版本中演变,并包含了更多有用的钩子函数(受Angular 2的启发)。让咱们深刻研究这些钩子函数并实际使用它们吧。这些钩子函数所带来的做用以及为何咱们须要使用它们,对于咱们深刻理解经过组件架构的应用具备重要的意义。html
在Angular v1.3.0+版本,我本身实现了.component()
方法,该方法深入得洞悉了怎么去使用这些生命周期函数以及这些函数在组件中的做用,让咱们开始研究它吧。java
什么是$onInit
?首先,他是Angular组件(译注:经过.component()
方法定义的组件)控制器中暴露出来的一个属性,咱们能够把一个函数赋值给该属性:git
var myComponent = { bindings: {}, controller: function () { this.$onInit = function() { }; } }; angular .module('app') .component('myComponent', myComponent);
$onInit
生命周期钩子用做控制器的初始化工做,下面举个经常使用例子:angularjs
var myComponent = { ... controller: function () { this.foo = 'bar'; this.bar = 'foo'; this.fooBar = function () { }; } };
注意上面的代码,咱们把全部的属性直接赋值到了this上面,它们就像“浮在”控制器的各个角落。如今,让咱们经过$onInit
来重写上面代码:github
var myComponent = { ... controller: function () { this.$onInit = function () { this.foo = 'bar'; this.bar = 'foo'; }; this.fooBar = function () { console.log(this.foo); // 'bar' }; } };
上面的数据明显地经过硬编码写入的,可是在实际的应用中,咱们一般是经过bindings: {}
对象来把咱们须要的数据传递到组件中,咱们使用$onInit
来进行一些初始化工做,这样就把之前那些“浮在”控制器各处的初始化变量都集中起来了,$onInit
就像是控制器中的constructor
,包含了一些初始化信息。json
对于this.fooBar
函数呢?不要着急,该函数放在$onInit
外面是彻底可以访问到初始化数据的,好比当你调用this.fooBar
的时候,函数会打印出this.foo
的值,也就是在$onInit
函数中定义的'bar'
。所以全部你初始化的数据都正确地绑定到了控制器的this
上下文中。数组
由于这些生命周期钩子定义得如此优雅(不一样的生命周期钩子都在组件的不一样生命周期被调用),一个组件也能够从另外的组件中继承方法,甚至继承的方法在$onInit
钩子中就能够直接使用。架构
首先咱们须要思考的是如何使用require
,我写过另一篇深刻介绍$onInit 和 require的文章,可是在此我依然会简要介绍一些require
的基本用法,随后将提供一个完整的实例。app
让咱们来看看myComponent
的例子,在这儿require
后面紧跟的是一个对象(只在.component()
方法中require字段后面接对象),当require
和.directive()
结合使用的时候,require字段后面也能够跟数组或者字符串语法形式。
var myComponent = { ... require: { parent: '^^anotherComponent' }, controller: function () { this.$onInit = function () { this.foo = 'bar'; this.bar = 'foo'; }; this.fooBar = function () { console.log(this.foo); // 'bar' }; } };
如上面的例子,require
被设置为^^anotherComponent
,require
值前面^^
表示自会在当前组件的父组件中搜寻anotherComponent
控制器,(若是require
值前面是^
那么首先会在当前组件搜寻是否有该控制器,若是没有再在其父组件中搜寻)这样咱们就能够在$onInit
中使用任何当定在父组件中的方法了。
var myComponent = { ... require: { parent: '^^anotherComponent' }, controller: function () { this.$onInit = function () { this.foo = 'bar'; this.bar = 'foo'; this.parent.sayHi(); }; this.fooBar = function () { console.log(this.foo); // 'bar' }; } };
注意,在Angular 1.5.6版本(见 CHANGELOG)中,若是require对象中属性名和require的控制器同名,那么就能够省略控制器名。这一特性并无带来给功能带来很大的改变,咱们能够以下使用它:
var myComponent = { ... require: { parent: '^^' }, controller: function () { ... } };
正如你所见,咱们彻底省略了须要requre的控制器名而直接使用^^
替代。完整写法^^parent
就被省略为^^
。须要谨记,在前面的一个例子中,咱们只能使用parent: '^^anotherComponent'
来表示咱们须要使用另一个组件中控制器中的方法(译者注:做者以上就是控制器和requre的属性名不相同时,不可以省略),最后,咱们只需记住一点,若是咱们想使用该条特性,那么被requre的控制器名必须和require的属性名同名。
让咱们使用$onInit
和require
来实现一个tabs
组件,首先咱们实现的组件大概如以下使用:
<tabs> <tab label="Tab 1"> Tab 1 contents! </tab> <tab label="Tab 2"> Tab 2 contents! </tab> <tab label="Tab 3"> Tab 3 contents! </tab> </tabs>
这意味着咱们须要两个组件,tab
和tabs
。咱们将transclude
全部的tabs
子元素(就是全部tab
模板中的tabs
元素)而后经过bindings
绑定的对象来获取label
值。
首先,组件定义了每一个组件都必须使用的一些属性:
var tab = { bindings: {}, require: {}, transclude: true, template: ``, controller: function () {} }; var tabs = { transclude: true, template: ``, controller: function () {} }; angular .module('app', []) .component('tab', tab) .component('tabs', tabs);
tab
组件须要经过bindings
绑定一些数据,同时在该组件中,咱们使用了require
,transclude
和一个template
,最后是一个控制器controller
。
tabs
组件首先会transclude
全部的元素到模板中,而后经过controller
来对tabs
进行管理。
让咱们来实现tab
组件的模板吧:
var tab = { ... template: ` <div class="tabs__content" ng-if="$ctrl.tab.selected"> <div ng-transclude></div> </div> `, ... };
对于tab
组件而言,咱们只在$ctrl.tab.selected
为true
的时候显示该组件,所以咱们须要一些在控制器中添加一些逻辑来处理该需求。随后咱们经过transclude
来对tab
组件中的内容填充。(这些内容就是展现在不一样tab内的)
var tabs = { ... template: ` <div class="tabs"> <ul class="tabs__list"> <li ng-repeat="tab in $ctrl.tabs"> <a href="" ng-bind="tab.label" ng-click="$ctrl.selectTab($index);"></a> </li> </ul> <div class="tabs__content" ng-transclude></div> </div> `, ... };
对于tabs
组件,咱们建立一个数组来展现$ctrl.tabs
内容,并对每个tab选项卡绑定click事件处理函数$ctrl.selectTab()
,在调用该方法是传入当前$index
。同时咱们transclude
全部的子节点(全部的<tab>
元素)到.tabs_content
容器中。
接下来让咱们来处理tab
组件的控制器,咱们将建立一个this.tab
属性,固然初始化该属性应该放在$onInit
钩子函数中:
var tab = { bindings: { label: '@' }, ... template: ` <div class="tabs__content" ng-if="$ctrl.tab.selected"> <div ng-transclude></div> </div> `, controller: function () { this.$onInit = function () { this.tab = { label: this.label, selected: false }; }; } ... };
你能够看到我在控制器中使用了this.label
,由于咱们在组件中添加了bindings: {label: '@'}
,这样咱们就可使用this.label
来获取绑定到<tab>
组件label
属性上面的值了(字符串形式)。经过这样的绑定形式咱们就能够把不一样的值映射到不一样的tab
组件上。
接下来让咱们来看看tabs
组件控制器中的逻辑,这可能稍微有点复杂:
var tabs = { ... template: ` <div class="tabs"> <ul class="tabs__list"> <li ng-repeat="tab in $ctrl.tabs"> <a href="" ng-bind="tab.label" ng-click="$ctrl.selectTab($index);"></a> </li> </ul> <div class="tabs__content" ng-transclude></div> </div> `, controller: function () { this.$onInit = function () { this.tabs = []; }; this.addTab = function addTab(tab) { this.tabs.push(tab); }; this.selectTab = function selectTab(index) { for (var i = 0; i < this.tabs.length; i++) { this.tabs[i].selected = false; } this.tabs[index].selected = true; }; }, ... };
咱们在$onInit
钩子处理函数中初始化this.tabs = []
,咱们已经知道$onInit
用来初始化属性值,接下来咱们定义了两个函数,addTab
和selectTab
。addTab
函数咱们会经过require传递到每个子组件中,经过这种形式来告诉父组件子组件的存在,同时保存一份对每一个tab的引用,这样咱们就能够经过ng-repeat
来遍历全部的tab选项卡,而且能够点击(经过selectTab
)选择不一样的选项卡。
接下来咱们经过tab
组件的require
来将addTab
方法委派到tab
组件中使用。
var tab = { ... require: { tabs: '^^' }, ... };
正如咱们在文章关于$onInit
和require
部分提到,咱们经过^^
来只requre父组件控制器中的逻辑而不在自身组件中寻找这些方法。除此以外,当咱们require的控制器名和requre对象中的属性名相同时咱们还能够省略requre的控制器名字,这是版本1.5.6新增长的一个特性。关于这一新特性准备好了吗?在下面代码中,咱们使用tabs: '^^'
,咱们有一个和require控制器同名的属性名{tabs: ...}
,这样咱们就能够在$onInit
中使用this.tabs
来调用父组件控制器中的方法了。
var tab = { ... require: { tabs: '^^' }, controller: function () { this.$onInit = function () { this.tab = { label: this.label, selected: false }; // this.tabs === require: { tabs: '^^' } this.tabs.addTab(this.tab); }; } ... };
把全部代码放一块儿:
var tab = { bindings: { label: '@' }, require: { tabs: '^^' }, transclude: true, template: ` <div class="tabs__content" ng-if="$ctrl.tab.selected"> <div ng-transclude></div> </div> `, controller: function () { this.$onInit = function () { this.tab = { label: this.label, selected: false }; this.tabs.addTab(this.tab); }; } }; var tabs = { transclude: true, controller: function () { this.$onInit = function () { this.tabs = []; }; this.addTab = function addTab(tab) { this.tabs.push(tab); }; this.selectTab = function selectTab(index) { for (var i = 0; i < this.tabs.length; i++) { this.tabs[i].selected = false; } this.tabs[index].selected = true; }; }, template: ` <div class="tabs"> <ul class="tabs__list"> <li ng-repeat="tab in $ctrl.tabs"> <a href="" ng-bind="tab.label" ng-click="$ctrl.selectTab($index);"></a> </li> </ul> <div class="tabs__content" ng-transclude></div> </div> ` };
点击选项卡相应内容就会呈现出来,当时,咱们并无设置一个初始化的展现的选项卡?这就是接下来$postLink
要介绍的内容。
咱们已经知道,compile
函数会返回一个pre
和post
‘连接函数’,如以下形式:
function myDirective() { restrict: 'E', scope: { foo: '=' }, compile: function compile($element, $attrs) { return { pre: function preLink($scope, $element, $attrs) { // access to child elements that are NOT linked }, post: function postLink($scope, $element, $attrs) { // access to child elements that are linked } }; } }
你也可能知道以下:
function myDirective() { restrict: 'E', scope: { foo: '=' }, link: function postLink($scope, $element, $attrs) { // access to child elements that are linked } }
当咱们只须要使用postLink
函数的时候,上面两种形式效果是同样的。注意咱们使用的post: function)() {...}
- 这就是咱们的主角。我已经在上面的代码中添加了一行注释“能够获取到已经连接的子元素”,上面的注释意味着在父指令的post 函数中,子元素的模板已经被编译而且已经被连接到特定的scope上。而经过compile
和pre
函数咱们是没法获取到已经编译、连接后的子元素的。所以咱们有一个生命周期钩子来帮我咱们在编译的最后阶段(子元素已经被编译和连接)来处理一些相应逻辑。
$postLink
给予了咱们处理如上需求的可能,咱们不需使用一些hack的范式就能够像以下形式同样使用$postLink
钩子函数。
var myComponent = { ... controller: function () { this.$postLink = function () { // fire away... }; } };
咱们已经知道,$postLink
是在全部的子元素被连接后触发,接下来让咱们来实现咱们的tabs
组件。
咱们能够经过$postLink
函数来给咱们的选项卡组件一个初始的选项卡。首先咱们须要调整一下模板:
<tabs selected="0"> <tab label="Tab 1">...</tab> <tab label="Tab 2">...</tab> <tab label="Tab 3">...</tab> </tabs>
如今咱们就能够经过bindings
获取到selected
特性的值,而后用以初始化:
var tabs = { bindings: { selected: '@' }, ... controller: function () { this.$onInit = function () { this.tabs = []; }; this.addTab = function addTab(tab) { this.tabs.push(tab); }; this.selectTab = function selectTab(index) { for (var i = 0; i < this.tabs.length; i++) { this.tabs[i].selected = false; } this.tabs[index].selected = true; }; this.$postLink = function () { // use `this.selected` passed down from bindings: {} // a safer option would be to parseInt(this.selected, 10) // to coerce to a Number to lookup the Array index, however // this works just fine for the demo :) this.selectTab(this.selected || 0); }; }, ... };
如今咱们已经有一个生动的实例,经过selected
属性来预先选择某一模板,在上面的例子中咱们使用selected=2
来预先选择第三个选项卡做为初始值。
在$postLink
函数中并非一个好的地方用以处理DOM操做。在Angular生态圈外经过原生的事件绑定来为HTML/template扩展行为,Directive依然是最佳选择。不要仅仅将Directive(没有模板的指令)重写为component组件,这些都是不推荐的作法。
那么$psotLint
存在的意义何在?你可能想在$postLink
函数中进行DOM操做或者自定义的事件。其实,DOM操做和绑定事件最好使用一个带模板的指令来进行封装。正确地使用$postLink
,你能够把你的疑问写在下面的评论中,我会很乐意的回复你的疑问。
这是一个很大的部分(也是最重要的部分),$onChanges
将和Angular 1.5.x中的组件架构及单向数据流一块儿讨论。一条金玉良言:$onChanges
在自身组件被改变可是却在父组件中发生的改变(译者注:其实做者这儿说得比较含糊,$onChange就是在单向数据绑定后,父组件向子组件传递的数据发生改变后会被调用)。当父组件中的一些属性发生改变后,经过bindings: {}
就能够把这种变化传递到子组件中,这就是$onChanges
的秘密所在。
在如下状况下$onChanges
会被调用,首先,在组件初始化的时候,组件初始化时会传递最初的changes
对象,这样咱们就能够直接获取到咱们所需的数据了。第二种会被调用的场景就是只当单向数据绑定<
he @
(用于获取DOM特性值,这些值是经过父组件传递的)改变时会被调用。一旦$onChanges
被调用,你将在$onChanges
的参数中获取到一个变化对象,咱们将在接下来的部分中详细讨论。
使用$onChanges
至关简单,可是该生命周期钩子又一般被错误的使用或谈论,所以咱们将在接下来的部分讨论$onChanges
的使用,首先,咱们声明了一个childConpoment
组件。
var childComponent = { bindings: { user: '<' }, controller: function () { this.$onChanges = function (changes) { // `changes` is a special instance of a constructor Object, // it contains a hash of a change Object and // also contains a function called `isFirstChange()` // it's implemented in the source code using a constructor Object // and prototype method to create the function `isFirstChange()` }; } }; angular .module('app') .component('childComponent', childComponent);
注意,这儿bindings
对象包含了一个值为'<'
的user
字段,该‘<’
表示了单向数据流,这一点在我之前的 文章已经提到过,单向数据流会致使$onChanges
钩子被调用。
可是,正如上面提到,咱们须要一个parentComponent
组件来完成个人实例:
var parentComponent = { template: ` <div> <child-component></child-component> </div> ` }; angular .module('app') .component('parentComponent', parentComponent);
须要注意的是:<child-compoent></component>
组件在<parent-component></parent-component>
组件中渲染,这就是为何咱们可以初始化一个带有数据的控制器,而且把这些数据传递给childComponent
:
var parentComponent = { template: ` <div> <a href="" ng-click="$ctrl.changeUser();"> Change user (this will call $onChanges in child) </a> <child-component user="$ctrl.user"> </child-component> </div> `, controller: function () { this.$onInit = function () { this.user = { name: 'Todd Motto', location: 'England, UK' }; }; this.changeUser = function () { this.user = { name: 'Tom Delonge', location: 'California, USA' }; }; } };
再次,咱们使用$onInit
来定义一些初始化数据,把一个对象赋值给this.user
。同时咱们有this.changeUser
函数,用来更新this.user
的值,这个改变发生在父组件,可是会触发子组件中的$onChange
钩子函数被调用,父组件的改变经过$onChanges来通知子组件,这就是$onChanges的做用。
如今,让咱们来看看childComponent
组件:
var childComponent = { bindings: { user: '<' }, template: ` <div> <pre>{{ $ctrl.user | json }}</pre> </div> `, controller: function () { this.$onChanges = function (changes) { this.user = changes; }; } };
这儿,咱们使用binding: {user: '<'}
,意味着咱们能够经过user
来接收来自父组件经过单向数据绑定传递的数据,咱们在模板中经过this.user
来展现数据的变化,(我经过使用| json
过滤器来展现整个对象)
点击按钮来观察childCompoent
经过$onChanges
来传播的变化:“我并无获取到变化??”像上面的代码,我永远也获取不到,由于咱们把整个变化对象都赋值给了this.user
,让咱们修改下上面的代码:
var childComponent = { ... controller: function () { this.$onChanges = function (changes) { this.user = changes.user.currentValue; }; } };
如今咱们可使用user
属性来获取到从父组件传递下来的数据,经过curentValue
来引用到该数据,也就是change对象上面的curentChange属性,尝试下上面的代码:
如今咱们已经从组件中获取到从单向数据绑定的数据,咱们能够在深刻的思考。虽然单项数据绑定并无被Angular所$watch
,可是咱们是经过引用传递。这意味着子组件对象(特别注意,简单数据类型不是传递引用)属性的改变依然会影响到父组件的相同对象,这就和双向数据绑定的做用同样了,固然这是无心义的。这就是,咱们能够经过设计。聪明的经过深拷贝来处理单向数据流传递下来的对象,来使得该对象成为“不可变对象”,也就是说传递下来的对象不会在子组件中被更改。
这个是一个fiddle例子(注意user | json
)过滤器移到了父组件中(注意,父组件中的对象也随之更新了)
做为替换,咱们可使用 angular.cocy()
来克隆传递下来的对象,这样就打破了JavaScript对象的“引用传递“:
var childComponent = { ... controller: function () { this.$onChanges = function (changes) { this.user = angular.copy(changes.user.currentValue); }; } };
作得更好,咱们添加了if
语句来检测对象的属性是否存在,这是一个很好的实践:
var childComponent = { ... controller: function () { this.$onChanges = function (changes) { if (changes.user) { this.user = angular.copy(changes.user.currentValue); } }; } };
甚至咱们还能够再优化咱们的代码,由于当父组件中数据发生变化,该变化会当即反应在this.user
上面,随后咱们经过深拷贝changes.user.currentValue
对象,其实这两个对象是相同的,下面两种写法实际上是在作同一件事。
this.$onChanges = function (changes) { if (changes.user) { this.user = angular.copy(this.user); this.user = angular.copy(changes.user.currentValue); } };
我更偏向于的途径(使用angular.copy(this.user)
)。
如今就开始尝试,经过深拷贝开复制从父组件传递下来的对象,而后赋值给子组件控制器相应属性。
感受还不错吧?如今咱们使用拷贝对象,咱们能够任意改变对象而不用担忧会影响到父组件(对不起,双向数据绑定真的不推荐了!)所以当咱们更新数据后,经过事件来通知父组件,单向数据流并非生命周期钩子的一部分,可是这$onChanges
钩子被设计出来的意思所在。数据输入和事件输出(输入 = 数据, 输出 = 事件),让咱们使用它吧。
上面咱们讨论了bindings
和$onChanges
已经覆盖了单向数据流,如今咱们将添加事件来扩展这一单向数据流。
为了使数据可以回流到 parentComponent
,咱们须要委托一个函数做为事件的回调函数,然咱们添加一个叫updateUser
的函数,该函数须要一个event
最为传递回来的参数,相信我,这样作将会颇有意义。
var parentComponent = { ... controller: function () { ... this.updateUser = function (event) { this.user = event.user; }; } };
从这咱们能够看出,咱们期待event
是一个对象,而且带有一个user
的属性,也就是从子组件传递回来的值,首先咱们须要把该事件回调函数传递到子组件中:
var parentComponent = { template: ` <div> ... <child-component user="$ctrl.user" on-update="$ctrl.updateUser($event);"> </child-component> </div> `, controller: function () { ... this.updateUser = function (event) { this.user = event.user; }; } };
注意我建立了一个带有on-*
前缀的特性,当咱们须要绑定一个事件(想一想 onclick/onblur)的时候,这是一个最佳实践。
如今咱们已经将该函数传递给了<child-component>
,咱们须要经过bindings
来获取这一绑定的函数。
var childComponent = { bindings: { user: '<', onUpdate: '&' // magic ingredients }, ... controller: function () { this.$onChanges = function (changes) { if (changes.user) { this.user = angular.copy(this.user); } }; // now we can access this.onUpdate(); } };
经过&
,咱们能够传递函数,因此咱们经过this.updateUser
字面量来把该函数从父组件传递到子组件,在子组件中更新的数据(经过在$onChanges
中深拷贝从bindings
对象中的属性)而后经过传递进来的回调函数来将更新后的数据传递回去,数据从父组件到子组件,而后经过事件回调将更新后的数据通知到父组件。
接下来,咱们须要扩展咱们的模板来时的用户能够更新深拷贝的数据:
var childComponent = { ... template: ` <div> <input type="text" ng-model="$ctrl.user.name"> <a href="" ng-click="$ctrl.saveUser();">Update</a> </div> `, ... };
这意味着咱们须要在控制器中添加this.saveUser
方法,让咱们添加它:
var childComponent = { ... template: ` <div> <input type="text" ng-model="$ctrl.user.name"> <a href="" ng-click="$ctrl.saveUser();">Update</a> </div> `, controller: function () { ... this.saveUser = function () { }; } };
尽管,当咱们在子组件中"保存"的时候,这其实仅仅是父组件回调函数的一个封装,所以咱们在子组件中直接调用父组件方法this.updateUser
(该方法已经绑定到了子组件onUpdate
属性上)
var childComponent = { ... controller: function () { ... this.saveUser = function () { // function reference to "this.updateUser" this.onUpdate(); }; } };
好的,相信我,咱们已经到了最后阶段,这也会使得事情变得更加有趣。相反咱们并非直接把this.user
传递到回调函数中,而是构建了一个$event
对象,这就像Angular 2同样(使用EventEmitter
),这也提供了在模板中使用$ctrl.updateUser($event)
来获取数据的一致性,这也就能够传递给子组件,$event
参数在Angular中是真实存在的,你能够经过ng-submit等指令来使用它,你是否还记得以下函数:(译者注:上面这一段翻译须要推敲)
this.updateUser = function (event) { this.user = event.user; };
咱们期待event对象带有一个user的属性,好吧,那就让咱们来在子组件中saveUser
方法中添加该属性:
var childComponent = { ... controller: function () { ... this.saveUser = function () { this.onUpdate({ $event: { user: this.user } }); }; } };
上面的代码看上去有些怪异。也许有一点吧,可是他是始终一致的,当你使用十遍之后,你就不再会中止使用它了。必要的咱们须要在子组件中建立this.saveUser
,而后在该方法中调用从父组件中经过bindings
传递进来的this.updateUsser
,接着咱们传递给它event对象,来把咱们更新后的数据返回给父组件:
尝试如上方式写代码吧:
这儿也有一个免费的教学视频,是我关于$onChanges
和单向数据流教程的一部分,你能够从这获取到 check it out here.
是的,单向数据绑定已经被认为是数据流的最佳方式,React,Angular 2 以及其余的类库都是用单向数据流,如今轮到Angualr 1了,虽然Angular 1加入单向数据流有些晚,可是依然很强大并将改变Angular 1.x应用开发方式。
$onChanges
还有一个特性,在changes
hash对象中,该对象实际上是SimpleChange
构造函数的一个实例,该构造函数原型对象上有一个isFirstChange
方法。
function SimpleChange(previous, current) { this.previousValue = previous; this.currentValue = current; } SimpleChange.prototype.isFirstChange = function () { // don't worry what _UNINITIALIZED_VALUE is :) return this.previousValue === _UNINITIALIZED_VALUE; };
这就是变化对象根据不一样的绑定策略怎么被创造出来(经过 new
关键字)(我之前实现过单向数据绑定,并享受这一过程)
你为何会想着使用该方法呢?上面咱们已经提到过,$onChanges
会在组件的某给生命周期阶段被调用,不只在父组件的数据改变时,(译者注:也在数据初始化的时候也会被调用)所以咱们能够经过该方法(isFirstChange
)来判断是非须要跳过初始化阶段,咱们能够经过在改变对象的某属性上面调用isFirstChange
方法来判断$onChanges
是不是第一次被调用。
this.$onChanges = function (changes) { if (changes.user.isFirstChange()) { console.log('First change...', changes); return; // Maybe? Do what you like. } if (changes.user) { this.user = angular.copy(this.user); } };
Here’s a JSFiddle if you want to check the console
.
咱们最后来讨论下最简单的一个生命周期钩子,$onDestroy
:
function SomeController($scope) { $scope.$on('$destroy', function () { // destroy event }); }
你能够猜测到该生命周期钩子怎么使用:
var childComponent = { bindings: { user: '<' }, controller: function () { this.$onDestroy = function () { // component scope is destroyed }; } }; angular .module('app') .component('childComponent', childComponent);
若是你使用了$postLink
来设置了DOM事件监听函数或者其余非Angular原生的逻辑,在$onDestroy
中你能够把这些事件监听或者非原生逻辑清理干净。
Angular 1.x 应用开发者的的开发模式也随着单向数据流,生命周期事件及生命周期钩子函数的出现而改变,不久未来我将发布更多关于组件架构的文章。