再談angularJS數據綁定機制及背後原理—angularJS常見問題總結

Angular 的數據綁定採用什麼機制,詳述原理?

髒檢查機制。闡釋髒檢查機制,必須先了解以下問題。html

單向綁定(ng-bind) 和 雙向綁定(ng-model) 的區別?

ng-bind 單向數據綁定($scope -> view),用於數據顯示,簡寫形式是 {{}}。前端

二者的區別在於頁面沒有加載完畢 {{val}} 會直接顯示到頁面,直到 Angular 渲染該綁定數據(這種行爲有可能將 {{val}} 讓用戶看到);而 ng-bind 則是在 Angular 渲染完畢後將數據顯示。git

ng-model 是雙向數據綁定($scope -> view and view -> $scope),用於綁定值會變化的表單元素等。angularjs

雙向數據綁定是 AngularJS 的核心機制之一。當 view 中有任何數據變化時,會更新到 model ,當 model 中數據有變化時,view 也會同步更新,顯然,這須要一個監控。github

 

雙向數據綁定的原理?

Angular 在 scope 模型上設置了一個 監聽隊列,用來監聽數據變化並更新 view 。web

   每次綁定一個東西到 view 上時 AngularJS 就會往 $watch 隊列裏插入一條 $watch,用來檢測它監視的 model 裏是否有變化的東西。    bootstrap

當你寫下表達式如{{ val }}時,AngularJS在幕後會爲你在scope模型上設置一個watcher(表達式將被 Angular 編譯成一個監視函數),它用來在數據發生變化的時候更新view。這裏的watcher和你會在AngularJS中設置的watcher是同樣的:segmentfault

$scope.$watch('val', function(newValue, oldValue) {
  //update the DOM with newValue
});
  • 將數據附加到 Scope 上,數據自身不會對性能產生影響,若是沒有監視器來監視這個屬性,那個這個屬性在不在 Scope 上是無關重要的;Angular 並不會遍歷 Scope 上的屬性,它將遍歷全部的觀察器。數組

  • 每一個監視函數是在每次 $digest 過程當中被調用的。所以,咱們要注意觀察器的數量以及每一個監視函數或者監視表達式的性能。瀏覽器

 

 

$digest循環是在何時以各類方式開始的?

當瀏覽器接收到能夠被 angular context 處理的事件時,$digest 循環就會觸發,遍歷全部的 $watch,最後更新 dom。

舉個栗子

<button ng-click="val=val+1">increase 1</button>

click 時會產生一次更新的操做(至少觸發兩次 $digest 循環)

  • 按下按鈕

  • 瀏覽器接收到一個事件,進入到 angular context

  • $digest 循環開始執行,查詢每一個 $watch 是否變化

  • 因爲監視 $scope.val 的 $watch 報告了變化,所以強制再執行一次 $digest 循環

  • 新的 $digest 循環未檢測到變化

  • 瀏覽器拿回控制器,更新 $scope. val.新值對應的 dom

 

在調用了$scope.$digest()後,$digest循環就開始了。假設你在一個ng-click指令對應的handler函數中更改了scope中的一條數據,此時AngularJS會自動地經過調用$digest()來觸發一輪$digest循環。當$digest循環開始後,它會觸發每一個watcher。這些watchers會檢查scope中的當前model值是否和上一次計算獲得的model值不一樣。若是不一樣,那麼對應的回調函數會被執行。調用該函數的結果,就是view中的表達式內容(譯註:諸如{{ val }})會被更新。除了ng-click指令,還有一些其它的built-in指令以及服務來讓你更改models(好比ng-model,$timeout等)和自動觸發一次$digest循環。

目前爲止還不錯!可是,有一個小問題。在上面的例子中,AngularJS並不直接調用$digest(),而是調用$scope.$apply(),後者會調用$rootScope.$digest()。所以,一輪$digest循環在$rootScope開始,隨後會訪問到全部的children scope中的watchers。

一般寫代碼時咱們無需主動調用 $apply 或 $digest 是由於 angular 在外部對咱們的回調函數作了包裝。例如經常使用的 ng-click,這是一個指令(Directive),內部實現則 相似 於

DOM.addEventListener('click', function ($scope) {
  $scope.$apply(() => userCode());
});

能夠看到:ng-click 幫咱們作了 $apply 這個操做。相似的不僅是這些事件回調函數,還有 $http、$timeout 等。我聽不少人抱怨說 angular 這個庫太大了什麼都管,其實你能夠不用它自帶的這些服務(Service),只要你記得手工調用 $scope.$apply。

如今,假設你將ng-click指令關聯到了一個button上,並傳入了一個function名到ng-click上。當該button被點擊時,AngularJS會將此function包裝到一個wrapping function中,而後傳入到$scope.$apply()。所以,你的function會正常被執行,修改models(若是須要的話),此時一輪$digest循環也會被觸發,用來確保view也會被更新。

 

Note: $scope.$apply()會自動地調用$rootScope.$digest()。$apply()方法有兩種形式。第一種會接受一個function做爲參數,執行該function而且觸發一輪$digest循環。第二種會不接受任何參數,只是觸發一輪$digest循環。咱們立刻會看到爲何第一種形式更好。

 

$digest 循環會運行多少次?

$digest 循環的上限是 10 次(超過 10次後拋出一個異常,防止無限循環)。

$digest 循環不會只運行一次。在當前的一次循環結束後,它會再執行一次循環用來檢查是否有 models 發生了變化。

這就是髒檢查(Dirty Checking),它用來處理在 listener 函數被執行時可能引發的 model 變化。所以 $digest 循環會持續運行直到 model 再也不發生變化,或者 $digest 循環的次數達到了 10 次(超過 10 次後拋出一個異常,防止無限循環)。

當 $digest 循環結束時,DOM 相應地變化。

髒檢查如何被觸發?

angular 會在可能觸發 UI 變動的時候進行髒檢查:這句話並不許確。實際上,

髒檢查是digest執行的,另外一個更經常使用的用於觸發髒檢查的函數apply——其實就是 $digest 的一個簡單封裝(還作了一些抓異常的工做)。

一般寫代碼時咱們無需主動調用 $apply 或 $digest 是由於 angular 在外部對咱們的回調函數作了包裝。例如經常使用的 ng-click,這是一個指令(Directive),內部實現則 相似於

DOM.addEventListener('click', function ($scope) {
  $scope.$apply(() => userCode());
});

angular對經常使用的dom事件,xhq事件做了封裝,若是調用這些封裝,就會在裏面觸發進入angular的digest流程,主要有如下狀況:

  • DOM事件,如用戶輸入文本,點擊按鈕等,(ng-click)

  • XHQ響應事件($http)

  • 瀏覽器Location變動事件,即Url中hash部分變動($location)

  • Timer事件($Timeout,$interval)

  • 手動調用$apply或$digest

 

$apply() 和$digest() 的區別?

 

$apply 是 $scope(或者是 direcvie 裏的 link 函數中的 scope)的一個函數,調用它會強制一次 $digest 循環(除非當前正在執行循環,這種狀況下會拋出一個異常,這是咱們不須要在那裏執行 $apply 的標誌)。

$apply() 和 $digest() 有兩個區別。

  • 1) 最直接的差別是, $apply 能夠帶參數,它能夠接受一個函數,而後在應用數據以後,調用這個函數。因此,通常在集成非 Angular 框架(好比jQuery)的代碼時,能夠把代碼寫在這個裏面調用。

  • 2) 當調用 $digest 的時候,只觸發當前做用域和它的子做用域上的監控,可是當調用 $apply 的時候,會觸發做用域樹上的全部監控。

何時手動調用 $apply() 方法?

取決因而否在 Angular 上下文環境(angular context)。

AngularJS對此有着很是明確的要求,就是它只負責對發生於AngularJS上下文環境中的變動會作出自動地響應(即,在$apply()方法中發生的對於models的更改)。AngularJS的built-in指令就是這樣作的,因此任何的model變動都會被反映到view中。可是,若是你在AngularJS上下文以外的任何地方修改了model,那麼你就須要經過手動調用$apply()來通知AngularJS。這就像告訴AngularJS,你修改了一些models,但願AngularJS幫你觸發watchers來作出正確的響應。

典型的須要調用 $apply() 方法的場景是:

  • 1) 使用了 JavaScript 中的 setTimeout() 來更新一個 scope model

  • 2) 用指令設置一個 DOM 事件 listener 而且在該 listener 中修改了一些 models

場景一

$scope.setMsg = function() {
    setTimeout(function() {
        $scope.message = 'hello world';
        console.log('message:' + $scope.message);
    }, 2000);
}
$scope.setMsg();

運行這個例子,會看到過了兩秒鐘以後,控制檯確實會顯示出已經更新的 model,然而,view 並無更新。

在 $scope.getMessage 加入 $apply() 方法。

$scope.getMessage = function() {
    setTimeout(function() {
        $scope.$apply(function() {
            $scope.message = 'hello world';
            console.log('message:' + $scope.message);
        });
    }, 2000);
}

再運行就 OK 了。

 

不過,在 AngularJS 中應該儘可能使用 $timeout Service 來代替 setTimeout(),由於前者會幫你調用 $apply(),讓你不須要手動地調用它。

場景二

實現一個 click 的指令,相似如下功能,directive 的編寫以下:

app.directive("inc", function() {
    return function (scope, element, attr) {
        element.on("click", function() {
            scope.val++;
        });
    };
});

 

跟場景一的結果同樣,這個時候,點擊按鈕,界面上的數字並不會增長。但查看調試器,發現數據確實已經增長了。

在 scope.val++; 一行後面添加 scope.$apply(); 或者 scope.$digest(); 就 OK 了。

$apply() 方法的兩種形式

//無參
$scope.$apply()
//有參
$scope.$apply(function(){
    ...
})

應該總使用接受一個 function 做爲參數的 $apply() 方法。這是由於當傳入一個 function 到 $apply() 中的時候,這個 function 會被包裝到一個 try…catch 塊中,因此一旦有異常發生,該異常會被 $exceptionHandler service 處理。

 

想象一下若是有個 alert 框顯示錯誤給用戶,而後有個第三方的庫進行一個網絡調用而後失敗了,若是不把它封裝進 $apply 裏面,Angular 永遠不會知道失敗了,alert 框就永遠不會彈出來了。

在 AngularJS 中使用 $watch注意事項?

若是要監聽的是一個對象,那還須要第三個參數

$scope.data.name = 'htf';
$scope.$watch('data', function(newValue, oldValue) {
    if (newValue === oldValue) { return; }
    $scope.updated++;
}, true);

表示比較的是對象的值而不是引用,若是不加第三個參數 true ,在 data.name 變化時,不會觸發相應操做,由於引用的是同一引用。

髒檢查的範圍

前面說到:angular 會對全部綁定到 UI 上的表達式作髒檢查。其實,在 angular 實現內部,全部綁定表達式都被轉換爲 $scope.$watch()。每一個 $watch 記錄了上一次表達式的值。有 ng-bind="a" 即有 $scope.$watch('a', callback),而 $scope.$watch 可不會管被 watch 的表達式是否跟觸發髒檢查的事件有關。

例如:

<div ng-show="false">
  <span id="span1" ng-bind="content"></span>
</div>
<span id="span2" ng-bind="content"></span>
<button ng-click="">TEST</button>

問:點擊 TEST 這個按鈕時會觸發髒檢查嗎?觸發幾回?

首先:ng-click="" 什麼都沒有作。angular 會由於這個事件回調函數什麼都沒作就不進行髒檢查嗎?不會。

而後:#span1 被隱藏掉了,會檢查綁定在它上面的表達式嗎?儘管用戶看不到,可是 $scope.$watch('content', callback) 還在。就算你直接把這個 span 元素幹掉,只要 watch 表達式還在,要檢查的還會檢查。

再次:重複的表達式會重複檢查嗎?會。

最後:別忘了 ng-show="false"。多是由於 angular 的開發人員認爲這種綁定常量的狀況並很少見,因此 $watch 並無識別所監視的表達式是不是常量。常量依舊會重複檢查。

因此:

答:觸發三次。一次 false,一次 content,一次 content

因此說一個綁定表達式只要放在當前 DOM 樹裏就會被監視,無論它是否可見,無論它是否被放在另外一個 Tab 裏,更無論它是否與用戶操做相關。

另外,就算在不一樣 Controller 裏構造的 $scope 也會互相影響,別忘了 angular 還有全局的 $rootScope,你還能夠 $scope.$emit。angular 沒法保證你絕對不會在一個 controller 裏更改另外一個 controller 生成的 scope,包括 自定義指令(Directive)生成的 scope 和 Angular 1.5 裏新引入的組件(Component)。

因此說不要懷疑用戶在輸入表單時 angular 會不會監聽頁面左邊導航欄的變化。

如何優化髒檢查與運行效率

髒檢查慢嗎?

說實話髒檢查效率是不高,可是也談不上有多慢。簡單的數字或字符串比較能有多慢呢?十幾個表達式的髒檢查能夠直接忽略不計;上百個也能夠接受;成百上千個就有很大問題了。綁定大量表達式時請注意所綁定的表達式效率。建議注意一下幾點:

  • 表達式(以及表達式所調用的函數)中少寫太過複雜的邏輯

  • 不要鏈接太長的 filter(每每 filter 裏都會遍歷而且生成新數組)

  • 不要訪問 DOM 元素。

一、使用單次綁定減小綁定表達式數量
單次綁定(One-time binding 是 Angular 1.3 就引入的一種特殊的表達式,它以 :: 開頭,當髒檢查發現這種表達式的值不爲 undefined 時就認爲此表達式已經穩定,並取消對此表達式的監視。這是一種行之有效的減小綁定表達式數量的方法,與 ng-repeat 連用效果更佳(下文會提到),但過分使用也容易引起 bug。

二、善用 ng-if 減小綁定表達式的數量

 

若是你認爲 ng-if 就是另外一種用於隱藏、顯示 DOM 元素的方法你就大錯特錯了。

ng-if 不只能夠減小 DOM 樹中元素的數量(而非像 ng-hide 那樣僅僅只是加個 display: none),每個 ng-if 擁有本身的 scope,ng-if 下面的 $watch 表達式都是註冊在 ng-if 本身 scope 中。當 ng-if 變爲 false,ng-if 下的 scope 被銷燬,註冊在這個 scope 裏的綁定表達式也就隨之銷燬了。

考慮這種 Tab 選項卡實現:

<ul>
  <li ng-class="{ selected: selectedTab === 1 }">Tab 1 title</li>
  <li ng-class="{ selected: selectedTab === 2 }">Tab 2 title</li>
  <li ng-class="{ selected: selectedTab === 3 }">Tab 3 title</li>
  <li ng-class="{ selected: selectedTab === 4 }">Tab 4 title</li>
</ul>
<div ng-show="selectedTab === 1">[[Tab 1 body...]]</div>
<div ng-show="selectedTab === 2">[[Tab 2 body...]]</div>
<div ng-show="selectedTab === 3">[[Tab 3 body...]]</div>
<div ng-show="selectedTab === 4">[[Tab 4 body...]]</div>

對於這種會反覆隱藏、顯示的元素,一般人們第一反應都是使用 ng-show 或 ng-hide 簡單的用 display: none 把元素設置爲不可見。

然而入上文所說,肉眼不可見不表明不會跑髒檢查。若是將 ng-show 替換爲 ng-if 或 ng-switch-when

<div ng-if="selectedTab === 1">[[Tab 1 body...]]</div>
<div ng-if="selectedTab === 2">[[Tab 2 body...]]</div>
<div ng-if="selectedTab === 3">[[Tab 3 body...]]</div>
<div ng-if="selectedTab === 4">[[Tab 4 body...]]</div>

有以下優勢:

  • 首先 DOM 樹中的元素個數顯著減小至四分之一,下降內存佔用

  • 其次 $watch 表達式也減小至四分之一,提高髒檢查循環的速度

  • 若是這個 tab 下面有 controller(例如每一個 tab 都被封裝爲一個組件),那麼僅當這個 tab 被選中時該 controller 纔會執行,能夠減小各頁面的互相干擾

  • 若是 controller 中調用接口獲取數據,那麼僅當對應 tab 被選中時纔會加載,避免網絡擁擠

固然也有缺點:

  • DOM 重建自己費時間

  • 若是 tab 下有 controller,那麼每次該 tab 被選中時 controller 都會被執行

  • 若是在 controller 裏面調接口獲取數據,那麼每次該 tab 被選中時都會從新加載

  • 各位讀者本身取捨。

三、給 ng-repeat 手工添加 track by

不恰當的 ng-repeat 會形成 DOM 樹反覆從新構造,拖慢瀏覽器響應速度,形成頁面閃爍。除了上面這種比較極端的狀況,若是一個列表頻繁拉取 Server 端數據自刷新的話也必定要手工添加 track by,由於接口給前端的數據是不可能包含 $$hashKey 這種東西的,因而結果就形成列表頻繁的重建。

其實沒必要考慮那麼多,總之加上沒壞處,至少能夠避免 angular 生成 $$hashKey 這種奇奇怪怪的東西。

具體參看: 詳解track by

 

髒檢測的利弊?

不少人對Angular的髒檢測機制感到不屑,推崇基於setter,getter的觀測機制,在我看來,這只是同一個事情的不一樣實現方式,並無誰徹底賽過誰,二者是各有優劣的。

 

你們都知道,在循環中批量添加DOM元素的時候,會推薦使用DocumentFragment,爲何呢,由於若是每次都對DOM產生變動,它都要修改DOM樹的結構,性能影響大,若是咱們能先在文檔碎片中把DOM結構建立好,而後總體添加到主文檔中,這個DOM樹的變動就會一次完成,性能會提升不少。

同理,在Angular框架裏,考慮到這樣的場景:

function TestCtrl($scope) {
    $scope.numOfCheckedItems = 0;

    var list = [];

    for (var i=0; i<10000; i++) {
        list.push({
            index: i,
            checked: false
        });
    }

    $scope.list = list;

    $scope.toggleChecked = function(flag) {
        for (var i=0; i<list.length; i++) {
            list[i].checked = flag;
            $scope.numOfCheckedItems++;
        }
    };
}

若是界面上某個文本綁定這個numOfCheckedItems,會怎樣?在髒檢測的機制下,這個過程毫無壓力,一次作完全部數據變動,而後總體應用到界面上。這時候,基於setter的機制就慘了,除非它也是像Angular這樣把批量操做延時到一次更新,不然性能會更低。

 

因此說,兩種不一樣的監控方式,各有其優缺點,最好的辦法是瞭解各自使用方式的差別,考慮出它們性能的差別所在,在不一樣的業務場景中,避開最容易形成性能瓶頸的用法。

ng-if跟ng-show/hide的區別有哪些?

第一點區別是,ng-if 在後面表達式爲 true 的時候才建立這個 dom 節點,ng-show 是初始時就建立了,用 display:block 和 display:none 來控制顯示和不顯示。第二點區別是,ng-if 會(隱式地)產生新做用域,ng-switch 、 ng-include 等會動態建立一塊界面的也是如此。

ng-repeat迭代數組的時候,若是數組中有相同值,會有什麼問題,如何解決?

會提示 Duplicates in a repeater are not allowed. 加 track by $index 可解決。固然,也能夠 trace by 任何一個普通的值,只要能惟一性標識數組中的每一項便可(創建 dom 和數據之間的關聯)。

ng-click中寫的表達式,能使用JS原生對象上的方法,好比Math.max之類的嗎?爲何?

不能夠。只要是在頁面中,就不能直接調用原生的 JS 方法,由於這些並不存在於與頁面對應的 Controller 的 $scope 中。除非在 $scope 中添加了這個函數:

$scope.parseInt = function(x){
    return parseInt(x);
}

{{now | 'yyyy-MM-dd'}}這種表達式裏面,豎線和後面的參數經過什麼方式能夠自定義?

定義方式:

app.filter('過濾器名稱',function(){
    return function(須要過濾的對象, 過濾器參數1, 過濾器參數2, ...){
        //...作一些事情
        return 處理後的對象;
    }
});

使用方式有兩種,一種是直接在頁面裏:

<p>{{now | date : 'yyyy-MM-dd'}}</p>

一種是在 js 裏面用:

// $filter('過濾器名稱')(須要過濾的對象, 參數1, 參數2,...)
$filter('date')(now, 'yyyy-MM-dd hh:mm:ss');

 factory和service,provider是什麼關係?

factory 把 service 的方法和數據放在一個對象裏,並返回這個對象;service 經過構造函數方式建立 service,返回一個實例化對象;provider 建立一個可經過 config 配置的 service。從底層實現上來看,service 調用了 factory,返回其實例;factory 調用了 provider,將其定義的內容放在 $get 中返回。factory 和 service 功能相似,只不過 factory 是普通 function,能夠返回任何東西(return 的均可以被訪問,因此那些私有變量怎麼寫你懂的);service 是構造器,能夠不返回(綁定到 this 的均可以被訪問);provider 是增強版 factory,返回一個可配置的 factory。

詳述angular的「依賴注入」

AngularJS 是經過構造函數的參數名字來推斷依賴服務名稱的,經過 toString() 來找到這個定義的 function 對應的字符串,而後用正則解析出其中的參數(依賴項),再去依賴映射中取到對應的依賴,實例化以後傳入。由於 AngularJS 的 injector 是假設函數的參數名就是依賴的名字,而後去查找依賴項,那若是像下面這樣簡單注入依賴,代碼壓縮後(參數被重命名了),就沒法查找到依賴項了。

function myCtrl = ($scope, $http){
    ...}

因此,一般會使用下面兩種方式注入依賴(對依賴添加的順序有要求)。

數組註釋法:

myApp.controller('myCtrl', ['$scope', '$http', function($scope, $http){
    ...
}])

顯式 $inject :

myApp.controller('myCtrl', myCtrl);
function myCtrl = ($scope, $http){
    ...
}
myCtrl.$inject = ['$scope', '$http'];

對於一個 DI 容器,必須具有三個要素:依賴項的註冊,依賴關係的聲明和對象的獲取。在 AngularJS 中,module 和 $provide 均可以提供依賴項的註冊;內置的 injector 能夠獲取對象(自動完成依賴注入);依賴關係的聲明,就是上面的那兩種方式。

html: {{currentDate()}} js: $scope.currentDate = function(){return new Date();} 這種寫法有沒有問題

有問題,時間是實時變化的,而後會一直更新數據,效率低,髒數據檢查到10次以後再也不繼續檢查;

解決方案:可使用一個變量來接收函數調用

controller as 和controller 有什麼區別,能解決什麼問題?

在使用controller的時候,爲控制器注入$window與$scope,這個時候controller中的屬性與方法是屬於$scope的,而使用controllerAS的時候,能夠將controller定義爲Javascript的原型類,在html中直接綁定原型類的屬性和方法

優勢:

  • 可使用 Javascript 的原型類, 咱們可使用更加高級的 ES6 或者 TypeScript 來編寫 Controller ;

  • 指代清晰。在嵌套scope時,子scope若是想使用父scope的屬性,只需簡單的使用父scope的別名引用父scope便可。

  • 避開了所謂的 child scope 原型繼承帶來的一些問題(原來別名ctrl就是定義在$scope上的一個對象,這就是controller的一個實例,全部在JS中定義controller時綁定到this上的model其實都是綁定到$scope.ctrl上的。使用controller as的一大好處就是原型鏈繼承給scope帶來的問題都不復存在了,即有效避免了在嵌套scope的狀況下子scope的屬性隱藏掉父scope屬性的狀況。)

  • controller的定義不依賴$scope。

  • 定義controller時不用顯式的依賴$scope,這有什麼好處呢?仔細看定義,這不就是一個普通的函數定義嘛,對!這就是好處!例子中的ScopeController就是所謂的POJO(Plain Old Javascript Object,Java裏偷來的概念),這樣的Object與框架無關,裏面只有邏輯。因此即使有一天你的項目再也不使用AngularJS了,依然能夠很方便的重用和移植這些邏輯。另外,從測試的角度看,這樣的Object也是單元測試友好的。單元測試強調的就是孤立其餘依賴元素,而POJO偏偏知足這個條件,能夠單純的去測試這個函數的輸入輸出,而不用費勁的去模擬一個假的$scope。

  • 防止濫用$scope的$watch,$on,$broadcast方法。可能剛剛就有人想問了,不依賴$scope我怎麼watch一個model,怎樣廣播和響應事件。答案是無法弄,這些事還真是隻有$scope能幹。但不少時候在controller裏watch一個model是不少餘的,這樣作會明顯的下降性能。因此,當你原本就依賴$scope的時候,你會習慣性的調用這些方法來實現本身的邏輯。但當使用controller as的時候,因爲沒有直接依賴$scope,使用watch前你會稍加斟酌,沒準就思考到了別的實現方式了呢。

  • 定義route時也能用controller as。除了在DOM中顯式的指明ng-controller,還有一種狀況是controller的綁定是route裏定義好的,那這時能使用controller as嗎?答案是確定的,route提供了一個controllerAs參數。這樣在模板裏就能夠直接使用別名home啦。

我的以爲仍是偏向於使用controller as的,固然有一點要澄清,使用contoller as並無什麼性能上的提高,僅僅是一種好的習慣罷了。

 

不管定義controller時有沒有直接依賴$scope,DOM中的scope是始終存在的。即便使用controller as,雙向綁定仍是經過$scope的watch以及digest來實現的。

 

請簡述$compile的用法?

angularjs裏比較重要但又不多手動調用的要屬$compile服務了,一般在寫組件或指令時,都是angularjs自動編譯完成的,但有時咱們可能須要手動編譯,好比封裝一個table組件,根據參數實現自定義渲染,增長一列複選框或者一列按鈕啥的,這是就須要用到$compile了。

$compile,在Angular中即「編譯」服務,它涉及到Angular應用的「編譯」和「連接」兩個階段,根據從DOM樹遍歷Angular的根節點(ng-app)和已構造完畢的 \$rootScope對象,依次解析根節點後代,根據多種條件查找指令,並完成每一個指令相關的操做(如指令的做用域,控制器綁定以及transclude等),最終返回每一個指令的連接函數,並將全部指令的連接函數合成爲一個處理後的連接函數,返回給Angluar的bootstrap模塊,最終啓動整個應用程序。

 

先解說下angular中頁面處理

ng對頁面的處理過程:

  • 瀏覽器把HTML字符串解析成DOM結構

  • ng把DOM結構給$compile,返回一個link函數

  • 傳入具體的scope調用這個link函數

  • 獲得處理後的DOM,這個DOM處理了指令,鏈接了數

 

$compile是個編譯服務。編譯服務主要是爲指令編譯DOM元素。

編譯一段HTML字符串或者DOM的模板,產生一個將scope和模板鏈接到一塊兒的函數。

$compile用法:

$compile(element,transclude,maxPriority);
  • element:將要被編譯和插入模板的元素或者HTML字符串

  • transclude:指令內有效的函數。Function(angular.Scope,cloneAttachFn=)

  • maxPriority:只有在指令比給定的優先級低時應用。隻影響根元素,不影響子元素

 .controller('MyController', function ($scope, $compile) {
                // 建立編譯函數
                var compileFn = $compile('<p>{{appCtrl.msg}}</p>');
                // 傳入scope,獲得編譯好的dom對象(已封裝爲jqlite對象)
                // 也能夠用$scope.$new()建立繼承的做用域
                var $dom = compileFn($scope);
                // 添加到文檔中
                $dom.appendTo('body');
            })

經過$compile服務能夠編譯html字符串或dom對象或jqLite對象,而後獲得一個編譯函數,再傳入$scope,就會在當前做用域進行編譯,返回編譯好的jqLite對象,這時就能夠直接添加到文檔中了(也能夠先添加到文檔再編譯)。

編譯的實質其實就是對dom對象解析,使dom對象與scope進行耦合,經過綁定能夠實現數據的更新,像Vue其實也是同樣的過程。

 

$compile解說推薦看《Angular中$compile源碼分析

 

這篇是對angularJS的一些疑點回顧,文章的問題大可能是從網上搜集整理而來,若有不妥之處或不遠被引用,請通知本人修改,謝謝!

轉載註明來源,謝謝!

參考文章:

相關文章
相關標籤/搜索