啟動(startup):
<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/angular-1.1.0.min.js"></script>
</head>
<body>
<p ng-init=" name='World' ">Hello {{name}}!</p>
</body>
</html>
- 瀏覽器載入HTML,然后把它解析成DOM樹。
- 瀏覽器載入angular.js腳本。
- AngularJS等到
DOMContentLoaded事件觸發執行。 - AngularJS尋找
ng-app指令,這個指令指示了應用程序的邊界。 - 使用
ng-app中指定的模塊來配置注入器($injector)。 - 注入器($injector)是用來創建“編譯服務($compile service)”和“根作用域($rootScope)”的。
- 編譯服務($compile service)是用來編譯DOM樹並把它鏈接到根作用域($rootScope)的,這里的根作用域就是html。
ng-init指令將“World”賦給作用域里的name這個變量。- 作用域中的name與頁面上的{{name}}綁定,整個表達式變成了“Hello World”。
執行(runtime):
瀏覽器的事件機制:
- 瀏覽器的Event loop等待事件的觸發。所謂事件包括用戶的交互操作、定時事件、或者網絡事件(服務器的響應)。
- 事件觸發后,如果有綁定事件回調函數,那么此函數就會被執行。此時會進入Javascript上下文。通常回調用來修改DOM結構。
- 一旦回調執行完畢,瀏覽器就會離開Javascript上下文,並且根據DOM的修改重新渲染視圖。
而AngularJS通過使用自己的Event loop,改變了傳統的Javascript工作流。這使得Javascript的執行被分成原生部分和擁有AngularJS執行上下文的部分。只有在AngularJS執行上下文中運行的操作,才能享受到AngularJS提供的數據綁定,異常處理,資源管理等功能和服務。你可以使用 $apply()方法,從普通Javascript上下文進入AngularJS執行上下文。記住,大部分情況下(如在控制器,服務中),$apply都已經被執行過了。只有當你使用自定義的事件回調或者是使用第三方類庫的回調時,才需要自己執行$apply。
<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/angular-1.1.0.min.js"></script>
</head>
<body>
<input ng-model="name">
<p>Hello {{name}}!</p>
</body>
</html>
在編譯階段:
input元素上的ng-model指令會給<input>輸入框綁定keydown事件;
{{name}}這個變量替換表達式建立了一個 $watch ,來接受 name 變量改變的通知。
在執行期階段:
按下任何一個鍵(以X鍵為例),都會觸發一個 input 輸入框的keydown事件;
input 上的指令捕捉到 input 內容的改變,然后調用 $apply("name = 'X';")來更新處於AngularJS執行上下文中的模型;
AngularJS將 name='X'應用到模型上;
$digest 循環開始;這個循環是由兩個小循環組成的,這兩個小循環用來處理$evalAsync隊列和$watch列表。這個$digest循環直到模型“穩定”前會一直迭代。這個穩定具體指的是$evalAsync列表為空,並且$watch列表中檢測不到任何改變了。這個$evalAsync隊列是用來管理那些“視圖渲染前需要在當前棧外執行的操作”。這通常使用 setTimeout(0)來完成的。並且,因為瀏覽器會根據事件隊列按順序渲染視圖,這時還會造成視圖的抖動。$watch列表是一個表達式的集合,這些表達式可能是自上次迭代后發生了改變的。如果檢測到了有改變,那么$watch函數就會被調用,它通常會把新的值更新到DOM中。
$watch 列表檢測到了name值的變化,然后通知 {{name}}變量替換的表達式,這個表達式負責將DOM進行更新;
AngularJS退出執行上下文,然后退出Javascript上下文中的keydown事件;
瀏覽器以更新的文本重新渲染視圖。
