angular原理及模塊簡介

Angular簡介(大神可略過)php

Angular是一個強大的前端框架,其強大之處主要是能夠把靜態頁面與動態數據綁定起來。平時咱們看到的網頁界面上面的數據都是固定,但若是咱們要變化這些數據,例如我在一個文本框輸入,要實時改動一個文本,腫麼破。這時候有兩種方法(我只想到兩種,求大神告知更多):html

1.改變一下,就請求一下後端,例如php,而後後端從新返回一個更新好的頁面,固然這種方法很傻,改變一點小數據就請求後端,的確太傻(因爲前端小白,我以前就用這種方式作了一個小網站,後來接觸到angular才發現本身太傻);前端

2.經過js改寫DOM(for 小白:這裏的DOM能夠理解爲html,但官方稱呼叫document object model,小白我之前老是不知道這是啥),js最初的document類就能夠幹這事兒,後來出現了一個jquery(js的一個強大的庫),也能夠方便的改寫DOM,對於這些前端就能夠知道的數據就不用請求後端了。jquery

jquery也只是一個庫,提供了一些簡單改變DOM的方法,對於簡單的小工程來講也夠了。可是對於比較大的工程,考慮的不只是功能的實現,還包括可維護可擴展,這就須要MVC模式了(for小白:至於啥是MVC,能夠看我介紹spring框架那篇博客裏面)。若是隻用jquery,view的邏輯會和c,m的邏輯混在一塊兒,不便於維護,例如你在文本框裏數據了一個東西,你得用寫代碼去獲取這個值,而後作處理,或者你的某個值改變了,你還得寫代碼去更新一下view,而angular就是提供這樣一個解決方案的框架(後面還會有介紹感受angular的強大)。git

Angular裏面的html文件就是view,叫模板(template),當你的數據變化須要改變模板的時候,不用再js代碼裏面去改變,你能夠什麼都不作,由於angular神奇的地方就是把模板與數據綁定(data binding),當數據改變的時候模板自動就變了,你的view變了(在文本框輸入東西了)也會自動反應到你的數據上面,這就是雙向綁定。在angular的理念裏面,模板就是一副素描畫,數據就是顏色,你想作完這幅畫,只須要向模板填充你想要顏色就好了(也就是填充你的數據),例以下面這個例子,你的輸入自動顯示到界面上angularjs

http://www.runoob.com/try/try.php?filename=try_ng_introgithub

你只須要專一你的數據和模板就夠了,他們之間怎麼填充,angular把這些作好了,也就是剝離了view層對contorller,mdoel層的影響,下面就是angular官方給出的區別web

通常處理數據:面試

angular:spring

引用自:https://docs.angularjs.org/guide/databinding

簡單來講就是你用angular了數據和view自動雙向綁定,不用你再代碼中去更新,不用angular你還要本身寫代碼在view變了時候去更新數據,在數據變了的時候去更新view。

 

angular原理

angular是基於js的一個框架,首先須要瞭解一下js的工做原理。

JS原理

瀏覽器裏面有一個事件隊列(event queue),用戶觸發啥事兒,或者網絡請求,延時操做(例如定時器之類),都是一個event,瀏覽器會輪訓這些事件,而後調用這些回調(這裏的回調簡單來講能夠理解爲觸發一個函數),而後就進入JavaScript的環境中執行(JavaScript context),在這裏面能夠改變數據,操做DOM(也就是html結構),而後再退出JavaScript環境,又進入瀏覽器環境,而後瀏覽器根據以前的改動從新繪製界面,這就是個一個流程。

下圖爲angular 官方解釋(引自https://docs.angularjs.org/guide/scope  )

angular原理

           angular的運行就是在JavaScript context裏面本身實現了一套環境,叫作angular環境(angular context),非angular那部分環境叫經典環境(classic context),

在angular context裏面也有一個隊列,這個隊列裏面是watch列表,列表裏面裝的就是那些被監聽的變量,包括那些進行數據綁定的變量(也就是和view進行綁定的那些)。若是用戶改變了一個綁定了數據的view,這時候會觸發一個angular函數$apply(也就是把這個event放入了event queue,而後輪訓到這個的時候就觸發了),而後把這個改變的值更新進綁定的那個變量,再開始調用一個digest的函數,digest就是用來輪訓這個watch列表,看這個列表中的指是否變更,若是有變更就變更改寫相應的DOM(不用angular就要本身寫這部分代碼,若是你有100個變量,你就要寫100個這種改動,並且若是之後有啥變更,還得本身去重構)。

關於angular原理機制的一些參考:

http://www.cnphp6.com/archives/64167

http://www.tuicool.com/articles/fAfiMv

這裏還有兩點注意,

1.angular會至少輪訓兩遍watch列表,爲啥?由於第一次輪訓可能在改寫DOM的時候可能會觸發其餘watch列表裏面的變量變化,這時候還會再輪訓,直到連續兩次輪訓的變量再也不變化。因此若是你有兩個變量的變化是相互影響的,就是A變了觸發B變,B變了觸發A變,這樣會引發死循環,angular好像是在輪訓5次(或者是10次,具體我忘了),若是還發現值沒有穩定,就會報錯(我曾經就幹過,界面忽然卡死,整個瀏覽器都卡死了,好不容易打開控制檯看,所有是angular輪訓報的錯,angular的輪訓直接卡死了整個瀏覽器)。

2.另外還有一點,關於效率問題,有人提出來angular這樣無差異輪訓可能會影響性能,可是angular的創始人給瞭解釋,人能在一個頁面上最多就能看200個元素,在一個web頁面上面不會有這麼多的元素綁定數據,若是綁定這麼多元素須要實時更新,那屬於網頁設計的問題(引自stackoverflow,具體網址找不到),因此並不用擔憂輪訓的效率問題,若是真的有效率問題,說明網頁自己可能存在問題(在豆瓣上看到一篇帖子,一我的用angular測了500個ngModel綁定的頁面,很卡,因此對於沒必要要的綁定,最好不要綁)

 

angular組件

Controller

           Controller是angular一個重要的組件,基本用angular必定會用到controller。Controller顧名思義,用來控制的,是MVC中的C,邏輯控制。在angular裏面,controller是一個JavaScript的構造函數,這個函數有兩個做用,初始化scope,還有就是增長方法(add behavior)。

引自:https://docs.angularjs.org/guide/controller

這裏稍微簡單解釋一下scope(後面會說一下),scope是一個對象(object)能夠理解爲是鏈接view和controller的一個橋樑,scope的屬性中有一些值,有一些方法(behavior),在html中能夠直接訪問到,以下圖,ng-click裏面的那些方法都是scope的一個屬性,還有顯示出來的那些值。在scope裏面初始化以後,在view(也就是html中,angular官方叫作template)裏面就就能夠訪問到這些值,也能夠觸發這些方法,這也就是angular數據雙向綁定的具體使用(很簡單吧,不用寫一堆jquery了)。

引自:https://docs.angularjs.org/guide/controller

           因此controller的做用就在於上面說的,初始化scope和爲scope增長方法,同時angular官方也給出了一些不建議使用的方式(以下圖),由於這樣操做基本上都有更好的方式

引自:https://docs.angularjs.org/guide/controller

Tips:

1.        angular在1.2版本後多了一個controlleras的語法,這個語法容許爲controller起個別名(有木有感受像sql裏面的as),以下圖

若是不用這個語法,須要在controller這個函數裏面依賴注入(Dependency Injection,後面也會介紹到,如上面那個例子所示,在函數的參數裏面寫個$scope)。若是用這個語法就不用了,二者的不一樣就在於不用controller as這個語法,html經過訪問scope的屬性來訪問數據,因此要把給html訪問的數據寫進scope的屬性,若是用controller as,整個controller這個實例(例子中的demo)會做爲scope的一個屬性,例如html要訪問一個data屬性的值

Controller as 訪問的是scope.demo.data

Controller 訪問的是scope.data

2.        controller繼承,每一個controller繼承實際上是scope的繼承,能夠簡單理解爲JavaScript的繼承,具體能夠看我另外一篇博客(若是有時間發的話T^T),或者是angular官方文檔上面說的。在這裏就是若是子controller裏面沒有指定這個數據,就會用父類的,若是指定了就用子類的,可是要注意若是是改變model裏面的值,有可能改父類的值(下面還會介紹這個概念)

 

 

Service

Service能夠理解爲MVC結構中的M層,來處理具體的業務邏輯,最理想的代碼就是在view裏面觸發了controller中的函數,而後controller來調用model裏面具體的處理,而後model返回給controller改scope的數據,反應在view上面。Service就是這個做用,在angular裏面,service有兩個特色

1.        懶加載(lazy loading):只有在須要用的時候(也就是在其餘service,filter,directive或者controller裏面依賴注入的時候纔會生成這個service實例)

2.        單例模式(singleton):service在angular裏面是單例(singleton),只在第一次被注入的時候建立實例,而後存在cache裏面,等須要的時候(也就是另外的依賴注入的時候),從cache裏面取出。因此service的生命週期只要建立以後,除非app退出,不然一直都有這個實例。不能銷燬(我還沒找到一種手動銷燬的辦法,事實上在網上查了一些須要銷燬的例子,其實均可以用其餘方法來作,不必定非要銷燬這個service實例)

http://stackoverflow.com/questions/32781488/how-to-destroy-an-angular-factory-instance

 

PS:若是在使用過程當中須要多例的樣子,能夠本身稍微改動一下,把service當作一個factory模式,返回各類須要的實例。能夠參考下面的鏈接(兩個例子其實同樣,只是在service的寫法上有點不一樣而已,另外提示連接裏面的網頁在embed標籤欄裏面能夠看到樣式)

http://jsfiddle.net/rbdmjLok/3/

http://jsfiddle.net/jeremylikness/rbdmjLok/

  • 註冊service

            在官方給出的developerguide裏面主要介紹了兩種方式,factory模式和provider模式,但其實還有一種service模式,因此總共有三種:

1.        factory:angular裏面比較經常使用的一種方式,註冊一個function,這個function在生成實例的時候會被調用到,這個函數返回一個service實例(要本身寫return的),因此只要把本身須要的service寫成一個obj,在這個obj裏面定義要的方法和值,而後再最後return一下這個obj就能夠了,以下圖

2.        service:service註冊就更簡單了,至關於對factory作了一層封裝。只要在service裏面寫須要的方法和值就好了,不須要return,以下圖

3.        provider:provider是angular裏面註冊service最底層的方式,不管是service方式註冊仍是factory註冊,其實底層都是用的provider這種方式。在provider裏面有一個$get的屬性,這個屬性是一個函數,這個函數就是factory裏面咱們寫的那個函數,用service那種寫法在這裏就是new 一個service的那個function賦給它(js裏面function也能夠看作一個對象的,因此等於直接new了一個對象給$get),angular就是經過在依賴注入(後面會介紹)的時候,調用這個函數得到一個實例。

若是一個service只有在實例化它以前才知道一個配置,例如讀文件或者網絡返回的一些動態配置,這種就不能在代碼裏面寫死,須要傳參數給service初始化(有點相似於Java,C++裏面的帶參數的構造函數),這時候就須要在provider裏面配置。因此provider就是在service初始化前對service作一些配置的組件(因此叫provider嘛),在provider裏面留一個接口(也就是留一個函數),等獲取到須要的配置的時候調provider的這個接口,就能夠設置service的參數。注意:這裏的provider只是提供了這樣一種方法,能夠把它理解爲一種工具,config纔是調用provider的東東。至於爲啥不直接用provider調用,而還要加個config,我一開始想不通,後來問了我boss,他認爲這只是爲了好理解,讓人一看就知道是配置。後來我也想了一下,應該也是爲了方便統一配置。若是直接調provider裏面建,要麼分開寫,要麼還要本身寫一個function在裏面配置,angular大概應該是爲了提供一個統一而且方便理解的接口吧(我的理解)。具體例子以下(注意:在provider裏面寫的名字,在用的時候會在名字後面加一個provider,例如例子中寫的User,但實際調用的那個provider是UserProvider)

 

在使用以上三種哪一種方式,官方文檔彷佛沒有給出啥太明顯的建議,我的是這樣認爲的,service更傾向因而一種服務,factory傾向於一個工具,provider是須要對這個service作一些配置。下面的博客寫得很好,裏面也有介紹啥時候用哪一種模式,附上一些參考。

https://my.oschina.net/tanweijie/blog/295067(上面幾張圖引用於此)

https://docs.angularjs.org/guide/services

http://stackoverflow.com/questions/15666048/angularjs-service-vs-provider-vs-factory?rq=1

 

scope

           scope是鏈接controller和view的橋樑,angular官方是這樣描述的:Scopeis the glue between application controller and the view(如上面的例子能夠看出)

  • scope對外api:

scope主要提供三個對外API(官方文檔中developer guide裏面只說起了兩個)

1.watch:

監聽model是否發生了變化,注意這裏的watch提供三種api監聽

(1)(scope.$watch(watchExpression, listener)) :只監聽對應的值或者reference是否變化,若是變化就觸發註冊的回調函數(也就是那個listener)

(2)(scope.$watchCollection(watchExpression, listener)) :監聽對應的值或者reference以及集合裏是否發生變化(例如集合增長或者減小,可是不包括集合裏面的值變化)

(3) (scope.$watch (watchExpression, listener, true)):監聽對應的值或者reference以及集合裏是否發生變化而且還包括裏面的值是否發生變化,下圖能夠比較清晰的看出其中的區別

2.apply:

在angular context外發布model變化的消息(PS:若是在angular context外變化angular是不會更新界面的,例如用setTimeOut這種方式來更新model,由於setTimeOut只是把一個event放入了隊列裏面,不會立刻執行,等到執行註冊timeout的這個function的時候,若是是徹底和angular無關的,也就是沒有用到angular的一些內置命令,這是不會觸發進入angular context的,因此這時候的運行徹底就是在angular context外,因此即便更新model的數據,也不會在view上面顯示出來,因此要在外面更新通常要本身調用一下apply,具體例子能夠參考下面給的那個博客。通常在ng開頭的命令中和angular自帶的一些service裏面都會自動調用apply,因此咱們不須要去調用)

3.digest:

這個在angular官方文檔中沒有列出來,但其實也是能夠直接調用的,官方應該是不推薦這樣作。調用apply就會調用digest,digest會輪訓那些watches(註冊了監聽的那些值的列表),若是發現值變化了會調用watch註冊的那個function來進行一些處理,能夠理解爲apply->digest->watch

參考自:http://www.cnphp6.com/archives/64167

  •  scope種類

           scope分爲兩種,一種是child scope,一種是isolatescope,前者是按照相似DOM結構的繼承關係,後者是徹底獨立的(通常用在directive中,由於directive通常是脫離上下文,可以單獨使用的,例如要作一個通用的列表,在用的時候只須要傳個列表值進來就能夠了,這種和上下文無關,因此通常是獨立的,就相似於Android裏面的adapter同樣)

  • scope繼承

           對於child scope的繼承,就和JavaScript的繼承差很少,簡單來講就是若是子scope中沒有的屬性,會去父scope去找,一層一層去找.

           若是這時候想賦值,不會改到父scope中的屬性,例如parentScope.a= 1,若是childScope中沒有指定a那麼childScope.a也是1,可是若是這時候賦值childScope.a = 2,這時候parentScope.a仍是1,爲啥,由於那個賦值語句對於JavaScript不是一個改變值的語句,是爲childScope建立了一個a的屬性值等於2,因此父parentScop不受影響。可是若是屬性是model(也就是對象)就不同了,覺得訪問model的時候傳的reference(這裏和C++是不同的,Java和JavaScript裏面都是把類做爲reference傳,C++是經過拷貝構造函數拷貝一份,除非修改拷貝構造函數,不然默認是傳值),例如:parentScope.a.value = 1,若是childScope沒有指定,那麼childScope.a.value也是1,這時候賦值childScope.a.value = 2,那麼這時候父parentScope.a.value也是2。爲啥,由於childScope.a是訪問parentScope的屬性(若是childScope裏面沒有指定,注意這個前提),因爲a是個model(對象),因此訪問的是地址(reference),這時候a.value=2就是對這個地址的值進行了改寫,因此parentScope也會被改變。固然若是先把childScope.a=newA,這樣childScope指向的就不是parentScope的了,這時候再改a的值就不會影響到parentScope了。建議能夠在本身在console裏面試試(JavaScript中function其實就是類,個人另一篇博客也介紹了關於JavaScript和angular的繼承關係)

參考自:

https://github.com/angular/angular.js/wiki/Understanding-Scopes

https://docs.angularjs.org/guide/scope

  • 追蹤scope

           這是angular官方給出的怎麼在view中調試scope,也就是看scope當前的一些值

           其實還有另外一個方法,也就是咱們在項目中用到的一個方法,設一個全局變量,而後再每一個controller裏面都把scope賦值給這個全局變量,這樣能夠在console裏面從這個全局變量裏面看到想追蹤的scope的值了。可是注意:若是這個全局變量只是爲了調試,不要在代碼中使用這個全局變量,也就是不要讀取,由於這個存在只是做爲調試使用的,是隨時會去掉的一個東西,若是有代碼邏輯依賴這個全局變量的值,在去掉以後會致使錯誤的。因此不要讓代碼依賴一個隨時會去掉的變量。

  • Scope事件分發

           這個在項目中咱們基本沒用過,但angular提供了這個機制,emit和broadcast(作Android的同窗應該對這個單詞比較有感受吧,但其實這相似於Android裏面的事件傳遞機制event dispatch,例如touchEvent和clickEvent這類,但angular這個彷佛不存在消費,由於項目沒用這個,因此也沒仔細考證,求大神告知)

emit:

釋放事件,當前scope和父scope均可以收到這個事件,若是在對應的scope裏面有註冊這個scope的回調,就會調用這個回調函數。

Broadcast:

發佈事件,當前scope和子scope都會收到這個事件,若是在對應的scope裏面有註冊這個scope的回調,就會調用這個回調函數。

具體例子可參考

https://docs.angularjs.org/guide/scopeScope Events Propagation部分

 

 

DependencyInjection(依賴注入)

           依賴注入是在不少編程語言和框架中都會說起的一個東西。其實也很好理解,首先什麼是依賴,A模塊(例如類,方法),須要用到B模塊中的東西,這時候就說A對B有依賴,例如A類裏面有個add的方法,在B類裏面須要用到這個這個add的方法,就是B對A有依賴。這時候就須要注入(其實注入也能夠理解爲初始化這種意思),在Java裏面可能就須要new 一個A,在angular裏面,就直接用函數參數這種形式來寫,可是要先在其餘地方定義這個依賴的類,就是要定義一個service(如上面提到的service那部分),而後再把這個service做爲一個函數參數傳進來。這樣就至關於new了這樣一個對象。具體能夠參考上面service部分的例子或者angular 官方的例子

依賴注入寫法在angular中有三種:

1.Inline ArrayAnnotation:

           在中括號裏面用單引號寫上,而且在function的參數裏面寫上,並且要注意順序一致

 

2.$inject PropertyAnnotation

           用$inject來寫,同時也要注意參數順序一致

3.ImplicitAnnotation

           只在function的參數裏面寫,最簡單的一種寫法,可是也是angular官方不推薦的。由於這種寫法在代碼混淆中會出問題,固然也有一個工具解決這個問題,這裏就不說起了,詳見angular官方文檔https://docs.angularjs.org/guide/di

另外angular還提供一種嚴苛模式(Android裏面其實也有一種嚴苛模式,可是和angular這個不一樣,Android的同窗不要弄混了),不容許Implicit Annotation,一旦用了Implicit Annotation就會報錯,這裏也就不介紹了,詳見angular官方文檔同上。另外關於解決依賴的問題,其實有三種方式(以下圖),但angular認爲前兩種方式很差,由於前兩種要本身編碼,比較麻煩,特別是在單元測試的時候,因此才用第三種(spring框架也是用的這種方式),angular裏面主要就是靠$injector來建立和追蹤依賴,因此減輕了開發者的負擔。更多詳情參考angular官方文檔,這裏就不說起了。

 

template

           angular裏面的template其實就是html,在angular中,能夠用下面四種方式來控制模板(html)的顯示(如圖),都比較簡單,看看例子就知道了,這裏就不說起了。angular建議若是是簡單的app,能夠把全部html寫在同一個html文件中,而後用directive這些來控制(通常就是index.html),若是比較複雜一點的app,能夠把不一樣的view(也就是html)放在同一個page裏面,可是把各自view定義在不一樣的html文件中,而後經過引用的方式在加載在這個page中(咱們的項目就是經過這種方式來作的,其實也能夠認爲是一個index.html,可是裏面的不少view都來自於其餘html文件)

參考自:

相關文章
相關標籤/搜索