數據雙向綁定並不是Angular最出彩的地方。大部分對AngularJs的介紹都偏重於使用,使用的學習只是學了AngularJs的API,而那只能AngularJs的很小一部分。隨着使用越來越深,系統越來越大,我們也越來越迷失,是時候深入AngularJs的實現來學習。因為AngularJs 2.0就要來了,由於2.0和1不兼容,基於API的學習不再有用,而內部實現的精華才能延續。其實,軟件的很多技術也大都如此,沖走的是實現,留下的是思想。
深入探索AngularJS
作用域Scope是DOM和Directives交互的抽象
Scope是POJO對象
AngularJS只是往Scope中添加了很多“內部"屬性,大部分以$開頭,還有些以$$開頭,兩個$開頭的屬性一般不要使用。
Scope是上下文
應該看作是一個容器,保存着當前的上下文和上下文敏感的變量數據等
Scope繼承樹
- Scope總是和一個DOM元素聯系起來
- Controller會創建一個新的Scope
- Directive有時候會創建一個新的Scope
- 其他情況會直接使用父級的Scope
- 如果不在ng-app內,沒有任何相關的Scope
Scope附加功能
AngularJS往Scope中添加了一些屬性
遍歷功能
- $id
Scope的唯一id號 - $root
- $parent
- $$childHead
- $$childTail
- $$prevSibling
- $$nextSibling
正交功能
Element和Attribute
Directive可以定義為Element(標識)也可以定義為標識的屬性。 更為強大的架構就是綜合應用這兩種功能,用屬性定義來改變或增強原Element的功能。
模塊模式 - Module Pattern
模塊模式是一個設計模式,它能夠消除大量重復的
this
和prototype
使用。 Angular Material就使用這個模式開發模塊代碼 Angular Material Coding Conventions and Guidelines
參考: http://toddmotto.com/mastering-the-module-pattern/
創建模塊
(function(){
//code
})();
這里申明了一個函數,然后馬上調用它自己,這也被稱作立即執行函數表達式 (Immediately-Invoked Function Expression)。這個函數就創建了一個新的作用域(Scope),從而模擬了類似私有域的效果, 把大部分代碼從全局作用域(Gloable Scope)中隔離出來。
創建新的作用域之后,我們需要把代碼賦於命名空間。
var Module = ( function () {
//code
})();
這樣,我們就在全局作用域中申明了 Module
,這樣我們就可以任意調用它,甚至把它傳給另外一個模塊。
私有方法 - Private Method
Javascript所有的函數定義默認下都是全局的,而且Javascript也沒有命名空間的概念,這兩個缺陷使得Javascript很容易產生名稱沖突。 模塊模式可以幫助解決這些問題。
Javascript 本身不能夠定義私有方法,但是我們可以使用模塊模式模擬出私有方法的效果。
私有方法本質上是:你不希望外部用戶調用執行的某個作用域內部的任何東西。特別是那些從服務器讀取或回寫的操作。
通過模塊模式,我們可以如下隱藏私有方法:
var Module = (function(){
var privateMethod = function(){
//do something
};
})();
在新作用域內部申明的方法privateMethod
就很好的隱藏起來,任何外部試圖調用privateMethod
都會導致錯誤。
理解返回 - Return
景點模塊模式可以用 return
返回一個對象給模塊,那些在該對象下聲明的方法可以通過模塊的命名空間來調用。
var Module = (function(){
return {
publicMethod:function(){
//code
}
};
})();
調用: Module.publicMethod();
這和標准方式定義的對象沒有任何區別:
var myObj = {
defaults: { name: 'Hao Wang'},
publicMethod: function () {
console.log(this.defaults);
}
};
//調用
myObj.publicMethod();
當時標准方式的問題是,一些內部屬性和方法都暴露出來了,不能隱藏(Javascript沒有私有屬性和方法)。 如上面例子中的defaults
就有可能被外部用戶修改,導致不期望的行為。
Promise - 異步的承諾
Promise讓異步調用看起來更像同步調用,從而很容易的取到返回值和捕獲異常。
介紹
使用Promise我們可以在任何一個執行點捕獲錯誤,然后忽略剩下的執行步驟。 這種流程控制來源於新的代碼風格本身,無需額外的代碼。 從而,我們可以很容易的組合多個函數功能並且異常以冒泡式的拋出,同時有維持異步運行的能力。
Promise自始自終都是異步運行,我們不用擔心它會阻塞其它部分的代碼運行。
Promise in Angular
Angular的事件循環(Event Loop)在$rootScope.$evalAsync階段解析(Resolve)Promise,直到$digest運行循環結束。 我們和容易的把Promise的結果輸出成視圖,這能夠直接把XHR調用的結果直接賦給$scope對象的一個屬性。
使用Promise到后台取數據的一個實例
<ul ng-controller="DashboardController">
<li ng-repeat="pr in pullRequests">
{{pr.title}}
</li>
</ul>
當用服務返回一個promise, 我們可以用.then()
方法與promise進行交互操作, 我們可在該方法中修改scope中的任何變量,從而改變視圖。
angular.module('myApp', [])
.controller('DashboardController', ['$scope', 'GithubService', function($scope, GithubService){
GithubService.getPullRequests(123) //這里返回的是Promise
.then(tunction(data){
$scope.pullRequests = data.data;
});
}]);
創建一個Promise
內建服務$q
可以用來創建你自己的Promise。我們可以通過調用$q.defer()
方法來創建一個“延遲”對象: var deferred = $q.defer();
“延遲”對象有三個方法和一個promise屬性,該屬性返回一個Promise對象。
.resolve(value)
- 解析(返回結果)方法.reject(reason)
- 拒絕方法;等同於解析出一個拒絕對象.resolve($q.reject(reason));
.notify(value)
- 返回執行的狀態方法
Promise執行狀態
如果我們有一個長時間運行的請求,可以調用.notify()
來及時返回進程狀態。 通常,我們會把這個長時間任務放在一個服務中:
.factory('GithubService', funcion($q, $http){
var getEventsFromRepo= function(){
//task
};
var service = {
makeMultipleRequests: function(repos){
for (var i=0; i < repos.length; i++) {
output.push(getEventsFromRepo(repos[i]));
percentComplete = (i+1) / repos.length * 100;
d.notify(percentComplete);
}
d.resolve(output);
return d.promise;
}
}
return service;
});
這里,每取一個repo, 我們就會收到一個進程狀態的通知。下面是對這個Pomise的使用和狀態通知的顯示
.controller('HomeController', function($scope, GithubService){
GithubService.makeMultipleRequests(['auser/behavior','..'])
.then(function(result){
//Handle the result
}, function(err){
//Error occurred
}, function(percentComplete) {
$scope.progress=percentComplte;
});
});
異步流
Promise的then()方法返回一個新的Promise, 這個新的Promise實際上是在原始Promise的值解析之后創建的。這樣我們就可以用then()讓異步的執行通過一個一個Promise串聯下去。
使用then()我們創建了異步執行體流,這樣我們可以在任何一步切入,然后切換不同的返回值。 這種切換也可以染我們暫停或者延遲解析的流程。
$http服務就是使用這種切入來實現請求和響應的interceptors。
$q服務還有幾個其他的幫助方法:
- $q.all(promises) - 可以把多個Promise合並成一個Promise。
- $q.defer() - 創建一個延遲對象。
- $q.reject(reason) - 創建一個Promise,其解析值為rejection
- $q.when(value) - 把一個對象封裝成為Promise,這個對象可以是普通對象也可以原本就是Promise。
與服務器的交互
談到后端時,我們有兩種情況:有服務器后端和無服務器后端
ExpressJS作服務器后端
搭建Express
- 安裝NodeJS
- 安裝Express:
$ npm install -g express-generator
- 創建網站:
$ express myApp
- 運行App准備:` cd myApp && npm install -d
- 運行App: `$ node app.js
- 高級運行App (源代碼的改動觸發重新編譯):
$ npm install --save-dev node mon
$ nodemon app.js
( 本文版權屬於© 2015 卓逸天成 | 轉載請注明作者和出處:卓逸知識文庫 )