初識angular框架后的所思所想
因為工作中實際開發(fā)需要,才開始接觸angular框架。從當(dāng)初的比葫蘆畫瓢,被各種問題、概念折磨摧殘,到現(xiàn)在有一定的了解認(rèn)識,覺得有必要將自己的認(rèn)識進(jìn)行簡單的總結(jié)。不到位的地方還望多多包涵。
1.雙向數(shù)據(jù)綁定
目前業(yè)內(nèi)盛行各種MV**框架,相關(guān)的框架不斷涌現(xiàn),而angular就是其中的一種(MVVM)。MV**框架其實最核心的問題就是將view層和model分離開來,降低代碼的耦合性,做到數(shù)據(jù)和表現(xiàn)的分離,MVC、MVP、MVVM均有相同的目標(biāo),而他們之間的不同就在于如何把model層和view關(guān)聯(lián)起來。
數(shù)據(jù)在model、view層如何流動就成了問題的關(guān)鍵,angular通過dirty-check實現(xiàn)了數(shù)據(jù)的雙向綁定。所謂的雙向綁定,就是view的變化可以反映到model層,而model數(shù)據(jù)的變化可以在view體現(xiàn)出來。那么angular是如何做到雙向綁定的?為何成為dirty-check呢?還是前端的一個原始問題出發(fā)吧:
html:
<input type="button" value="increase 1" id="J-increase" /> <span id="J-count"></span>
js:
<script> var bindDate = { count: 1, appy: function () { document.querySelector('#J-count').innerHTML = this.count; }, increase: function () { var _this = this; document.querySelector('#J-increase').addEventListener('click', function () { _this.count++; appy(); }, true); }, initialize: function () { // 初始化 this.appy(); // this.increase(); } }; bindDate.initialize(); </script>
在上面的例子中,存在兩個過程:
view層影響model層: 頁面上點擊button,造成數(shù)據(jù)count的數(shù)量增加1
model層反應(yīng)view層: count發(fā)生完變化以后,通過apply函數(shù)來反映到view層上
這是以前使用jquery、YUI等類庫實現(xiàn)的數(shù)據(jù)處理,這里面存在的問題很明顯:
- 涉及到了大量的DOM操作;
- 過程繁瑣;
- 代碼耦合性太高,不便于寫單元測試。
下面來看看angular是如何進(jìn)行數(shù)據(jù)處理的:
第一步. 添加watcher:就是當(dāng)數(shù)據(jù)發(fā)生變化的時候,需要檢測哪些對象,需要先進(jìn)行注冊
// 對angular里面的源碼進(jìn)行了精簡 $watch: function(watchExp, listener, objectEquality) { var scope = this, array = scope.$$watchers, watcher = { fn: listener, last: initWatchVal, get: get, exp: watchExp, eq: !!objectEquality }; if (!array) { array = scope.$$watchers = []; } array.unshift(watcher); }
第二步. dirty-check:就是當(dāng)有某個scope作用域下的數(shù)據(jù)發(fā)生變化后,需要遍歷檢測注冊的$$watchers = [...]
$digest: function() { while (length--) { watch = watchers[length]; watch.fn(value, lastValue, scope); } }
這樣就實現(xiàn)了數(shù)據(jù)的雙向綁定,上面的實現(xiàn)是否跟自定義事件很像呢?可以看到使用了觀察者設(shè)計模式或(publisher-subscriber)。
2.依賴注入
使用過spring框架的同學(xué)都知道,Ioc、AOP是spring里面最重要的兩個概念,而Ioc就可以里面為注入依賴(DI),很明顯angular帶有非常濃厚的后端色彩。
同樣,首先來看下不使用DI,是如何解決對象相互依賴的:
function Car() { ... } Car.prototype = { run: function () {...} } function Benz() { var cat = new Car(); } Benz.prototype = { ... }
在上面的例子中,類Benz依賴于類Car,直接通過內(nèi)部New來解決這種依賴關(guān)系。這樣做的弊端非常明顯,代碼耦合性變高,不利于維護(hù)。后端框架很早就意識到了這個問題,spring早期通過在xml文件中注冊對象之間的依賴關(guān)系,后來有通過anotation的方式更加方便地解決DI問題,COS端的同學(xué)可以看看后端的代碼。
js語言本身是不具有注解(annotation)機制的,那angular是如何實現(xiàn)的呢?
1.模擬注解
// 注解的模擬 function annotate(fn, strictDi, name) { var $inject; if (!($inject = fn.$inject)) { $inject = []; $inject.push(name); }else if (isArray(fn)) { $inject = fn.slice(0, last); } return $inject; } createInjector.$$annotate = annotate;
2. 注入對象的創(chuàng)建
function createInjector(modulesToLoad, strictDi) { //通過singleton模式創(chuàng)建對象 var providerCache = { $provide: { provider: supportObject(provider), factory: supportObject(factory), service: supportObject(service), value: supportObject(value), constant: supportObject(constant), decorator: decorator } }, instanceCache = {}, instanceInjector = (instanceCache.$injector = createInternalInjector(instanceCache, function(serviceName, caller) { var provider = providerInjector.get(serviceName + providerSuffix, caller); return instanceInjector.invoke(provider.$get, provider, undefined, serviceName); })); return instanceInjector; }
3. 獲取注入對象
function invoke(fn, self, locals, serviceName) { var args = [], $inject = annotate(fn, strictDi, serviceName); for (...) { key = $inject[i]; // 替換成依賴的對象 args.push( locals && locals.hasOwnProperty(key) ? locals[key] : getService(key, serviceName) ); } if (isArray(fn)) { fn = fn[length]; } return fn.apply(self, args); }
到這里,是否是看到很多后端框架設(shè)計的思路,沒有anotation就模擬一個,難怪PPK要說angular是" a front-end framework by non-front-enders for non-front-enders"
3.controller通信
在實際開發(fā)中,應(yīng)用系統(tǒng)會非常龐大,一個應(yīng)用app不可能只存在一個controller,那么不同controller之間就存在通信的可能,如何解決這個常見問題,主要有兩種方法:
1.事件機制: 把事件注冊在$rootScope上,這樣做的問題就是會在$rootScope上注冊太大事件,會引起一些列后續(xù)問題
//controller1 app.controller('controller1', function ($rootScope) { $rootScope.$on('eventType', function (arg) { ...... }) }) // controller2 app.controller('controller2', function ($rootScope) { $rootScope.$emit('eventType',arg); or $rootScope.$broadcast('eventType',arg); })
2.通過service: 充分利用angular的DI特性,利用service是單例的特點,在不同controller之間起到橋梁作用
// 注冊service app.service('Message', function () { return { count: void(0); } }) // controller1,修改service的count值 app.controller('controller1', function ($scope, Message) { $scope.count = 1; Message.count = $scope.count; }); // controller2, 獲取service的count值 app.controller('controller2', function ($scope, Message) { $scope.num = Message.count; });
4.service的特點
1. 單例(singleton): angular里面只有service才可以進(jìn)行DI諸如,controller、directive這些均不具有這些功能,service字面上就是提供一些基本的服務(wù),跟具體的業(yè)務(wù)沒有關(guān)聯(lián),而controller、directive則與具體業(yè)務(wù)緊密相關(guān)聯(lián),所以需要保證service的唯一性。
2. lazy new:angular首先會生成service的provider,但是并沒有立即生成對應(yīng)的service,只有到需要這些服務(wù)的時候才會進(jìn)行實例化操作。
3. provider)的分類: provider()、factory、service、value、constant,其中provider是最底層的實現(xiàn),其他方式都是在其基礎(chǔ)上的語法糖(sugar),需要注意的是這些服務(wù)最終均要添加$get方法,因為具體service是通過執(zhí)行$get方法生成的。
5. directive的實現(xiàn)
directive的編譯(compiler)包括兩個階段: compile、link。簡單來講compile階段主要處理template DOM,此時并不涉及作用域問題,也就是沒有進(jìn)行數(shù)據(jù)渲染,例如ngRepeate指令就是通過compile進(jìn)行template修改的,執(zhí)行compile后會返回link函數(shù),覆蓋后面定義的link函數(shù);而link主要是進(jìn)行數(shù)據(jù)渲染,分為pre-link和post-link兩個環(huán)節(jié),這兩個環(huán)節(jié)解析的順序是相反,post-link是先解析內(nèi)部,然后才是外部,這樣對directive的解析就是安全的,因為directive內(nèi)部還可以包括directive,同時link是對真正DOM的處理,會涉及DOM操作的性能問題。
本文涉及的內(nèi)容還不是很全民,之后還會有相應(yīng)補充,希望大家也可以對angular框架進(jìn)行學(xué)習(xí)探討。
相關(guān)文章
AngularJS實現(xiàn)與后臺服務(wù)器進(jìn)行交互的示例講解
今天小編就為大家分享一篇AngularJS實現(xiàn)與后臺服務(wù)器進(jìn)行交互的示例講解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08AngularJS $on、$emit和$broadcast的使用
本文主要介紹AngularJS $on、$emit和$broadcast的使用,這里整理了詳細(xì)的資料及簡單示例代碼有興趣的小伙伴可以參考下2016-09-09Angular8引入百度Echarts進(jìn)行圖表分析的實現(xiàn)代碼
這篇文章主要介紹了Angular8引入百度Echarts進(jìn)行圖表分析的實現(xiàn)代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11AngularJS基礎(chǔ) ng-options 指令詳解
本文主要介紹AngularJS ng-options 指令,這里對ng-options指令的知識做了詳細(xì)整理,并附有詳細(xì)的代碼示例,有需要的小伙伴可以參考下2016-08-08AngularJS入門教程之 XMLHttpRequest實例講解
本文主要講解 AngularJS XMLHttpRequest,這里給大家整理相關(guān)資料并提供實例代碼,有需要的小伙伴參考下2016-07-07