angular問題總結

1.雙向數據綁定

目前業內盛行各類MV*框架,相關的框架不斷涌現,而angular就是其中的一種(MVVM)。MV*框架其實最核心的問題就是將view層和model分離開來,下降代碼的耦合性,作到數據和表現的分離,MVC、MVP、MVVM均有相同的目標,而他們之間的不一樣就在於如何把model層和view關聯起來。html

數據在model、view層如何流動就成了問題的關鍵,angular經過dirty-check實現了數據的雙向綁定。所謂的雙向綁定,就是view的變化能夠反映到model層,而model數據的變化能夠在view體現出來。那麼angular是如何作到雙向綁定的?爲什麼成爲dirty-check呢?仍是前端的一個原始問題出發吧:前端

html:<input type="button" value="increase 1" id="J-increase" />jquery

<span id="J-count"></span>git

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>

在上面的例子中,存在兩個過程:angularjs

  • view層影響model層: 頁面上點擊button,形成數據count的數量增長1github

  • model層反應view層: count發生完變化之後,經過apply函數來反映到view層上spring

這是之前使用jquery、YUI等類庫實現的數據處理,這裏面存在的問題很明顯:後端

  • 涉及到了大量的DOM操做設計模式

  • 過程繁瑣安全

  • 代碼耦合性過高,不便於寫單元測試。下面來看看angular是如何進行數據處理的:

第一步. 添加watcher:就是當數據發生變化的時候,須要檢測哪些對象,須要先進行註冊

// 添加watcher
// 對angular裏面的源碼進行了精簡  
$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:就是當有某個scope做用域下的數據發生變化後,須要遍歷檢測註冊的$$watchers = [...]

// dirty-check
$digest: function() {
    while (length--) {
        watch = watchers[length];
        watch.fn(value, lastValue, scope);
    }
}

這樣就實現了數據的雙向綁定,上面的實現是否跟自定義事件很像呢?能夠看到使用了觀察者設計模式或(publisher-subscriber)。

2.依賴注入

使用過spring框架的同窗都知道,Ioc、AOP是spring裏面最重要的兩個概念,而Ioc就能夠裏面爲注入依賴(DI),很明顯angular帶有很是濃厚的後端色彩。

一樣,首先來看下不使用DI,是如何解決對象相互依賴的:

// 傳統對象依賴解決辦法
function Car() {
    ...
}
Car.prototype = {
    run: function () {...}
}
 
function Benz() {
    var cat = new Car();
}
Benz.prototype = {
    ...
}

在上面的例子中,類Benz依賴於類Car,直接經過內部New來解決這種依賴關係。這樣作的弊端很是明顯,代碼耦合性變高,不利於維護。後端框架很早就意識到了這個問題,spring早期經過在xml文件中註冊對象之間的依賴關係,後來有經過anotation的方式更加方便地解決DI問題,COS端的同窗能夠看看後端的代碼。

js語言自己是不具備註解(annotation)機制的,那angular是如何實現的呢?

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. 注入對象的建立

// 注入對象的建立
function createInjector(modulesToLoad, strictDi) {
    //經過singleton模式建立對象
    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);
}

到這裏,是不是看到不少後端框架設計的思路,沒有anotation就模擬一個,難怪PPK要說angular是" a front-end framework by non-front-enders for non-front-enders"

3.controller通訊

在實際開發中,應用系統會很是龐大,一個應用app不可能只存在一個controller,那麼不一樣controller之間就存在通訊的可能,如何解決這個常見問題,主要有兩種方法:

1.事件機制: 把事件註冊在$rootScope上,這樣作的問題就是會在$rootScope上註冊太大事件,會引發一些列後續問題

// 事件解決controller通訊
//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解決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才能夠進行DI諸如,controller、directive這些均不具備這些功能,service字面上就是提供一些基本的服務,跟具體的業務沒有關聯,而controller、directive則與具體業務緊密相關聯,因此須要保證service的惟一性。

2.lazy new: angular首先會生成service的provider,可是並無當即生成對應的service,只有到須要這些服務的時候纔會進行實例化操做。

3.provider的分類: provider()、factory、service、value、constant,其中provider是最底層的實現,其餘方式都是在其基礎上的語法糖(sugar),須要注意的是這些服務最終均要添加$get方法,由於具體service是經過執行$get方法生成的。

5.directive的實現

directive的編譯(compiler)包括兩個階段: compile、link。簡單來說compile階段主要處理template DOM,此時並不涉及做用域問題,也就是沒有進行數據渲染,例如ngRepeate指令就是經過compile進行template修改的,執行compile後會返回link函數,覆蓋後面定義的link函數;而link主要是進行數據渲染,分爲pre-link和post-link兩個環節,這兩個環節解析的順序是相反,post-link是先解析內部,而後纔是外部,這樣對directive的解析就是安全的,由於directive內部還能夠包括directive,同時link是對真正DOM的處理,會涉及DOM操做的性能問題。

圖片描述

參考

https://angularjs.org/

https://github.com/xufei/blog/issues/10

http://teropa.info/blog/2013/11/03/make-your-own-angular-part-1-scopes-and-digest.html

http://www.quirksmode.org/blog/archives/2015/01/the_problem_wit.html

http://www.jvandemo.com/the-nitty-gritty-of-compile-and-link-functions-inside-angularjs-directives/

相關文章
相關標籤/搜索