AngularJS 工作原理詳解
個(gè)人覺得,要很好的理解AngularJS的運(yùn)行機(jī)制,才能盡可能避免掉到坑里面去。在這篇文章中,我將根據(jù)網(wǎng)上的資料和自己的理解對(duì)AngularJS的在啟動(dòng)后,每一步都做了些什么,做一個(gè)比較清楚詳細(xì)的解析。
首先上一小段代碼(index.html),結(jié)合代碼我們來看看,angular一步一步都做了些什么。
<!doctype html> <html ng-app> <head> <script src="angular.js"></script> </head> <body> <png-init=" name='World' ">Hello {{name}}!</p> </body> </html>
當(dāng)你用瀏覽器去訪問index.html的時(shí)候,瀏覽器依次做了如下一些事情:
- 加載html,然后解析成DOM;
- 加載angular.js腳本;
- AngularJS等待DOMContentLoaded事件的觸發(fā);
- AngularJS尋找ng-app指令,根據(jù)這個(gè)指令確定應(yīng)用程序的邊界;
- 使用ng-app中指定的模塊配置$injector;
- 使用$injector創(chuàng)建$compile服務(wù)和$rootScope;
- 使用$compile服務(wù)編譯DOM并把它鏈接到$rootScope上;
- ng-init指令對(duì)scope里面的變量name進(jìn)行賦值;
- 對(duì)表達(dá)式{{name}}進(jìn)行替換,于是乎,顯示為“Hello World!”
整個(gè)過程可以用這張圖來表示:
好了,通過上面的例子我們清楚了AngularJS是怎樣一步一步渲染出一個(gè)頁面的。那么它又是如何和瀏覽器的事件回路來交互的呢?或者說是如何跟用戶來交互的呢?粗略來講,主要分為三個(gè)階段:
1. 瀏覽器的事件回路一直等待著事件的觸發(fā),事件包括用戶的交互操作、定時(shí)事件或者網(wǎng)絡(luò)事件(如服務(wù)器的響應(yīng)等);
2. 一旦有事件觸發(fā),就會(huì)進(jìn)入到Javascript的context中,一般通過回調(diào)函數(shù)來修改DOM;
3. 等到回調(diào)函數(shù)執(zhí)行完畢之后,瀏覽器又根據(jù)新的DOM來渲染新的頁面。
正如下面一張圖所示,交互過程主要由幾個(gè)循環(huán)組成:
AngularJS修改了一般的Javascript工作流,并且提供了它自己的事件處理機(jī)制。這樣就把Javascript的context分隔成兩部分,一部分是原生的Javascript的context,另一部分是AngularJS的context。只有處在AngularJS的context中的操作才能享受到Angular的data-binding、exception handling、property watching等服務(wù),但是對(duì)于外來者(如原生的Javascript操作、自定義的事件回調(diào)、第三方的庫等)Angular也不是一概不接見,可以使用AngularJS提供的$apply()函數(shù)將這些外來者包進(jìn)AngularJS的context中,讓Angular感知到他們產(chǎn)生的變化。
接下來,讓我們一起來看看交互過程中的這幾個(gè)循環(huán)是怎么工作的?
1. 首先,瀏覽器會(huì)一直處于監(jiān)聽狀態(tài),一旦有事件被觸發(fā),就會(huì)被加到一個(gè)event queue中,event queue中的事件會(huì)一個(gè)一個(gè)的執(zhí)行。
2. event queue中的事件如果是被$apply()包起來的話,就會(huì)進(jìn)入到AngularJS的context中,這里的fn()是我們希望在AngularJS的context中執(zhí)行的函數(shù)。
3. AngularJS將執(zhí)行fn()函數(shù),通常情況下,這個(gè)函數(shù)會(huì)改變應(yīng)用的某些狀態(tài)。
4. 然后AngularJS會(huì)進(jìn)入到由兩個(gè)小循環(huán)組成的$digest循環(huán)中,一個(gè)循環(huán)是用來處理$evalAsync隊(duì)列(用來schedule一些需要在渲染視圖之前處理的操作,通常通過setTimeout(0)實(shí)現(xiàn),速度會(huì)比較慢,可能會(huì)出現(xiàn)視圖抖動(dòng)的問題)的,一個(gè)循環(huán)是處理$watch列表(是一些表達(dá)式的集合,一旦有改變發(fā)生,那么$watch函數(shù)就會(huì)被調(diào)用)的。$digest循環(huán)會(huì)一直迭代知道$evalAsync隊(duì)列為空并且$watch列表也為空的時(shí)候,即model不再有任何變化。
5. 一旦AngularJS的$digest循環(huán)結(jié)束,整個(gè)執(zhí)行就會(huì)離開AngularJS和Javascript的context,緊接著瀏覽器就會(huì)把數(shù)據(jù)改變后的視圖重新渲染出來。
接下來,我們還是結(jié)合代碼來解析一下:
<!doctype html> <html ng-app> <head> <script src="angular.js"></script> </head> <body> <input ng-model="name"> <p>Hello {{name}}!</p> </body> </html>
這段代碼和上一段代碼唯一的區(qū)別就是有了一個(gè)input來接收用戶的輸入。在用瀏覽器去訪問這個(gè)html文件的時(shí)候,input上的ng-model指令會(huì)給input綁上keydown事件,并且會(huì)給name變量建議一個(gè)$watch來接收變量值改變的通知。在交互階段主要會(huì)發(fā)生以下一系列事件:
1. 當(dāng)用戶按下鍵盤上的某一個(gè)鍵的時(shí)候(比如說A),觸發(fā)input上的keydown事件;
2. input上的指令察覺到input里值的變化,調(diào)用$apply(“name=‘A'”)更新處于AngularJS的context中的model;
3. AngularJS將'A'賦值給name;
4. $digest循環(huán)開始,$watch列表檢測到name值的變化,然后通知{{name}}表達(dá)式,更新DOM;
5. 退出AngularJS的context,然后退出Javascript的context中的keydown事件;
6. 瀏覽器重新渲染視圖。
以上就是對(duì)AngularJS 工作原理的資料整理,后續(xù)繼續(xù)補(bǔ)充相關(guān)資料,謝謝大家對(duì)本站的支持!
相關(guān)文章
Angular4自制一個(gè)市縣二級(jí)聯(lián)動(dòng)組件示例
本篇文章主要介紹了Angular4自制一個(gè)市縣二級(jí)聯(lián)動(dòng)組件示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-11-11angularJs中$http獲取后臺(tái)數(shù)據(jù)的實(shí)例講解
今天小編就為大家分享一篇angularJs中$http獲取后臺(tái)數(shù)據(jù)的實(shí)例講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-08-08Angular6使用forRoot() 注冊(cè)單一實(shí)例服務(wù)問題
這篇文章主要介紹了Angular6使用forRoot() 注冊(cè)單一實(shí)例服務(wù)問題,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-08-08Angular.js中$apply()和$digest()的深入理解
相信大家都知道$digest()和$apply()是AngularJS中的兩個(gè)核心并且有時(shí)候容易引人誤解的部分。我們需要深入理解這兩者是如何運(yùn)作的,從而才能理解AngularJS本身是如何運(yùn)作的。本文的目的就是介紹$digest()和$apply()是如何確確實(shí)實(shí)的對(duì)你有用的。下面來一起看看吧。2016-10-10