AngularJs學習筆記--Scope

來源:互聯網
上載者:User

標籤:事件傳播   from   rtm   element   經理   struct   org   item   img   

原版地址:http://code.angularjs.org/1.0.2/docs/guide/scope

 

一、什麼是Scope?

  scope(http://code.angularjs.org/1.0.2/docs/api/ng.$rootScope.Scope)是一個指嚮應用model的object。它也是expression(http://www.cnblogs.com/lcllao/archive/2012/09/16/2687162.html)的執行內容。scope被放置於一個類似應用的DOM結構的階層中。scope可以監測(watch,$watch)expression和傳播事件。

 

二、scope的特性

  • scope提供$watch API(http://code.angularjs.org/1.0.2/docs/api/ng.$rootScope.Scope#$watch),用於監測model的變化。
  • scope提供$apply API(http://code.angularjs.org/1.0.2/docs/api/ng.$rootScope.Scope#$apply),在“Angular realm”(controller、server、angular event handler)之外,從系統到視圖傳播任何model的變化。
  • scope可以在提供到被共用的model屬性的訪問的時候,被嵌入到獨立的應用組件中。scope通過(原型),從parent scope中繼承屬性。
  • scope在expression求值之時提供上下文環境。例如,{{username}}運算式是無意義的,除非它與一個特定的定義了”username”屬性的scope一起進行求值。

 

三、Scope as Data-Model(scope作為資料模型)

  scope是在應用controller與view之間的紐帶。在模版linking(http://www.cnblogs.com/lcllao/archive/2012/09/04/2669802.html)的階段,directive(http://www.cnblogs.com/lcllao/archive/2012/09/09/2677190.html)在scope中設定$watch運算式。$watch讓directive能夠得知屬性的變化,使得directive將更新後的值渲染到DOM中。

  controller和directive兩者都與scope有引用,但它們兩者之間沒有(引用)(Both controllers and directives have reference to the scope, but not to each other)。這樣的安排,將controller從directive和DOM中隔離開來。這是一個重要的地方,因為它讓controller與view是隔離的,極大地提升了應用的可測試性(greatly improves the testing story of the applications)。

<!DOCTYPE HTML><html lang="zh-cn" ng-app><head>    <meta charset="UTF-8">    <title>data-model</title>    <style type="text/css">        .ng-cloak {            display: none;        }    </style></head><body class="ng-cloak"><div ng-controller="MyController">    你的名字: <input type="text" ng-model="username"/>    <button ng-click="sayHello()">歡迎</button>    <hr/>    {{greeting}}</div><script src="../angular-1.0.1.js" type="text/javascript"></script><script type="text/javascript">    function MyController($scope) {        $scope.username = "My Little Dada";        $scope.sayHello = function() {            $scope.greeting = "Hello~" + $scope.username + "!";        };    }</script></body></html>

 

  在上面的例子中我們可以注意到MyController以”My Little Dada”對scope中的username屬性進行賦值。然後,scope通知input進行賦值,將username的值預先填入input中。這展示了controller如何做才能夠寫入資料到scope中。

  相似地,controller可以將行為附加在scope中,正如那個當使用者點擊“歡迎”按鈕時觸發的sayHello方法一樣。sayHello方法可以讀取username屬性,也可以建立greeting屬性。這表明,當它們綁定到HTML input控制項時,scope中的屬性會自動更新。

  邏輯上,顯示{{greeting}}涉及以下兩點:

  • 與定義了{{greeting}}運算式的模版DOM節點一起檢索scope。在這個例子中,這個scope與傳遞到MyController中的scope是相同的。(我們在稍後將會討論scope的階層)
  • 通過之前檢索的scope,對greeting運算式進行求值,然後將結果作為封閉DOM元素的text的值。

  我們可以認為,scope和它自己的屬性可以作為資料,用於渲染視圖。scope是所有和view相關的東西單一的真相來源(The scope is the single source-of-truth for all things view related)。

  從可測試性來看,controller和view的分離是值得欣喜的,因為它允許我們在沒有渲染細節的幹擾下(專註於)測試行為。

   

it(‘should say hello‘, function() {    var scopeMock = {};    var cntl = new MyController(scopeMock);       // Assert that username is pre-filled    expect(scopeMock.username).toEqual(‘World‘);        // Assert that we read new username and greet    scopeMock.username = ‘angular‘;    scopeMock.sayHello();    expect(scopeMock.greeting).toEqual(‘Hello angular!‘);});

 

四、Scope Hierarchies(scope階層)

  每一個angular應用有且只有一個root scope,但可以擁有多個child scope。

  應用可以擁有多個child scope,因為一些directive會建立新的child scope(參考directive文檔,查看哪些directive可建立新的scope,如ng-repeat)。當新的scope被建立後,他們將作為一個child scope,加入到parent scope中。這樣,建立了一個與它們附屬的DOM相似的樹結構。

  當angular對{{username}}求值時,它首先查看與當前元素關聯的scope的username屬性。如果沒有找到對應的屬性,它將會一直向上搜尋parent scope,直到到達root scope。在javascript中,這個行為被稱為“原型繼承”,child scope典型地繼承自它們的parent。

  這個例子說明應用中的scope(是怎樣的),屬性的原型繼承。

<!DOCTYPE HTML><html lang="zh-cn" ng-app><head>    <meta charset="UTF-8">    <title>scope-hierarchies</title>    <style type="text/css">        .ng-cloak {            display: none;        }        .ng-scope {            border: 1px dashed red;        }    </style></head><body class="ng-cloak"><div ng-controller="MyController">    經理:{{employee.name}} [{{department}}] <br/>    報告:    <ul>        <li ng-repeat="employee in employee.reports">            {{employee.name}} [{{department}}]        </li>    </ul>    <hr/>    {{greeting}}</div><script src="../angular-1.0.1.js" type="text/javascript"></script><script type="text/javascript">    function MyController($scope) {        $scope.department = "某部";        $scope.employee = {            name:"My Little Dada",            reports: [                {name:"Lcllao"},                {name:"那個誰^o^"}            ]        };    }</script></body></html>

  注意,angular自動放置ng-scope class到與scope黏附的元素中。<style>定義在上面的例子中,通過紅色的虛線,高亮新的scope的範圍。因為repeater對{{employee.name}}運算式求值,child scope是必須的,但取決於運算式在哪一個scope進行求值,不同的scope有不同的結果。相似地,{{department}}的值是從root scope中原型繼承得來的,只有在那個地方有,才有department屬性的定義。

 

五、Retrieving Scopes from the DOM(從DOM中檢索scope)

  scope作為$scope資料屬性附加到DOM中,可以被用於以調試作為目的的檢索。(在應用中通過這個方式檢索Scope是不可能的。)附加到的DOM的root scope的位置是通過ng-app directive的位置定義的。通常ng-app是放置在<html>元素中,但它也可以放置在其他元素中,例如,只有一部分視圖需要被angular控制。

  在debugger中查看scope:

1. 在瀏覽器中,對著感興趣的元素點擊右鍵,選擇“查看元素”。我們可以看到瀏覽器debugger高亮了我們選中的元素。

2. debugger允許我們在console中通過$0變數去訪問當前選擇的元素。

3. 想查看關聯的scope,我們可以在console中輸入:angular.element($0).scope()

 

六、Scope Events Propagation(Scope事件傳播)

  scope可以以類似於DOM事件的方式進行事件傳播。事件可以被broadcast(http://code.angularjs.org/1.0.2/docs/api/ng.$rootScope.Scope#$broadcast)到child scope或者emit(http://code.angularjs.org/1.0.2/docs/api/ng.$rootScope.Scope#$emit)到parent scope中。(當前scope如果有監聽,也會執行)

<!DOCTYPE HTML><html lang="zh-cn" ng-app><head>    <meta charset="UTF-8">    <title>scope-event-propagation</title>    <style type="text/css">        .ng-cloak {            display: none;        }    </style></head><body class="ng-cloak"><div ng-controller="MyController">    root scope count:{{count}}    <ul>        <li ng-repeat="i in [1]" ng-controller="MyController">            <button ng-click="$emit(‘MyEvent‘)">$emit("MyEvent")</button>            <button ng-click="$broadcast(‘MyEvent‘)">$broadcast("MyEvent")</button>            <br/>            middle scope count:{{count}}            <ul>                <li ng-repeat="item in [1,2]" ng-controller="MyController">                    Leaf scope count:{{count}}                </li>            </ul>        </li>    </ul></div><script src="../angular-1.0.1.js" type="text/javascript"></script><script type="text/javascript">    function MyController($scope) {        $scope.count = 0;        $scope.$on("MyEvent", function() {            $scope.count++;        });    }</script></body></html>

 

七、Scope Life Cycle(scope生命週期)

  瀏覽器正常的事件流中,當瀏覽器接收到事件後,它會執行一個相應的javascript回調。一旦回呼函數執行完畢後,瀏覽器將會重繪DOM,並返回到繼續等待事件的狀態。

  當瀏覽器在angular執行環境外調用javascript代碼時,這意味著angular是不知道model的改變的。要正確處理model的修改,這個命令必須通過使$apply方法進入angular執行環境。只有在$apply方法中的model變更,才會正確地被angular統計。例如,一個directive監聽了DOM事件,例如ng-click,它必須在$apply方法中對錶達式進行求值。

  在對錶達式求值之後,$apply方法執行一個$digest。在$digest階段裡,scope檢查所有$watch監聽的運算式,將現在的值與舊的值作比較。髒檢查(dirty checking)是非同步。這意味著指派陳述式(例如$scope.username=”angular”)將不會馬上導致一個$watch被通知,反而,$watch的通知將會延遲到$digest階段。這個延遲是必須的,因為它把多個model更新聯合到一個$watch通知中,這保證了在$watch通知的過程中,沒有其他$watch在執行。如果一個$watch改變了model的值,那麼它將會強制增加一個$digest周期。

  1) Creation(建立scope)

    root scope是在應用啟動的過程中,被$injector(http://code.angularjs.org/1.0.2/docs/api/AUTO.$injector)建立的。在模版linking的過程中,一些directive會建立新的child scope。

  2) Watcher registration(註冊watcher)

    在模版linking過程中,directive在scope中註冊$watch。這些watch將會被用作向DOM傳播model的值。

  3) Model mutation(Model變化)

    為了讓變化被正確地檢測,我們需要將他們包裹在scope.$apply中。(angular API 已經隱式地做了這部操作,所以,當在controller中做同步的工作或者與$http或者$timeout一起做非同步工作的時候,不需要額外的$apply調用)。

  4) Mutation observation(變化監測)

    在$apply的結尾,angular會在root scope執行一個$digest周期,這將會傳播到所有child scope中。在$digest周期中,所有註冊了$watch的運算式或者function都會被檢查,判斷model是否發生了改變,如果改變發生了,那麼對應的$watch監聽器將會被調用。

  5) Scope destruction(scope銷毀)

    當child scope不再是必須的時候,child scope的產生者有責任通過scope.$destroy() API銷毀它們(child scope)。這將會停止$digest的調用傳播傳播到child scope中,讓被child scope model使用的記憶體可以被gc(garbage collector)回收。

1. Scopes and Directives

  在編譯階段中,compiler依靠DOM模版匹配directive。directive通常可以分為兩大類:

  • 觀察型directive(Observing directives),例如dobule-curly運算式{{expression}},使用$watch方法註冊監聽器。無論什麼時候,運算式(的值)發生改變,這類directive必須被通知,從而更新view。
  • 監聽型directive(Listener directive),例如ng-click,註冊一個監聽器到DOM中。當DOM的監聽器觸發時,directive會執行相關的運算式,並通過使用$apply方法更新視圖。

  當一個外部的事件(例如使用者動作、timer或者XHR)被監聽到,相關的expression必須通過$apply方法應用到scope中,讓所有監聽器能夠正確地更新。

2. Directives that Create Scopes

  在大多數的情況中,directive和scope是相互影響的,但不會建立新的scope執行個體。然而,一些directive(例如ng-controller和ng-repeat)會建立新scope,附加child scope到對應的DOM元素中。我們通過使用angular.element(aDomElement).scope()查看任意DOM元素的scope。

3. Controllers and Scopes

  在以下的情況中,scope與controller是相互影響的:

  • controller使用scope暴露controller方法到模版中(查看ng-controller(http://code.angularjs.org/1.0.2/docs/api/ng.directive:ngController))。
  • controller定義方法(行為),可以改變model(scope上的屬性)。
  • controller可能在model中註冊watch。這些watch會在controller行為執行之後馬上執行。

4. Scope $watch Performance Considerations(Scope $watch的效能考慮)

  在angular中,為了檢測屬性的變化而對scope進行髒檢測(Dirty checking),是一個普遍的操作。為此,這要求dirty checking函數必須是高效的。應小心dirty checking函數不要做任何DOM訪問操作,因為DOM訪問的速度比訪問javascript對象屬性的速度要慢好幾個數量級。

AngularJs學習筆記--Scope

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.