這篇博客其實是angular源碼閱讀之路的一個必經(jīng)站點,就是要理解injector,provider,module之間的關(guān)系——這關(guān)系其實就是依賴注入的本質(zhì)。

那么請專注地看下面這一段話吧:

通俗一點的理解:

module是發(fā)布任務(wù)的BOSS。

injector是領(lǐng)取任務(wù)的中間人。

provider是真正去執(zhí)行任務(wù)的馬仔。

當(dāng)然上面這一段話只是比喻,不太嚴謹,可是很形象。待我慢慢解釋來。

 

如果你比較熟悉angular,那么你肯定知道在每一個module對象上,都有一個私有屬性"_invokeQueue"。

這個_invokeQueue,其實就是module發(fā)布的任務(wù)。

怎么理解『_invokeQueue,其實就是module發(fā)布的任務(wù)?!贿@句話呢?請看下面的簡單小代碼。

當(dāng)我執(zhí)行下面這段語句,我會在myapp中創(chuàng)建一個全局變量name='不咬人的蚊子':

1
2
//注冊了一個全局變量name='不咬人的蚊子'
angular.module('myapp').constant('name','不咬人的蚊子');

而這個變量'name'我可以在controller里面這樣使用:

1
2
3
4
angular.module.controller('myctr',['$scope','name',function($scope,name){
    console.log(name)//不咬人的蚊子
    $scope.name  = name;
}])

現(xiàn)在說回_invokeQueue,當(dāng)我執(zhí)行了那個注冊全局變量的constant方法的時候,其實是module發(fā)布了一個任務(wù),這個任務(wù)保存在_invokeQueue里面。

注意:其實這時候只是發(fā)布任務(wù),任務(wù)并沒有被執(zhí)行。這時候_invokeQueue里面是這樣的:

1
2
3
module._invokeQueue=[
    ['constant',['name','不咬人的蚊子']]//數(shù)組里面包含著另一個數(shù)組。
]

對,沒錯,這就是Module發(fā)布的任務(wù),invokeQueue其實就是一個數(shù)組,里面有著一系列任務(wù)(這里只是拿constant舉例,其實在真實案例中,還會有各種任務(wù),比如controller啊什么的)。

invokeQueue這個數(shù)組里面的每一個元素都是一個任務(wù),如你所見,這任務(wù)也是一個數(shù)組。

任務(wù)數(shù)組的第1個元素(下標為0)記錄了這個任務(wù)具體是什么任務(wù),是constant,還是controller,還是directive等等。

任務(wù)數(shù)組的第2個元素(下標為1)記錄了執(zhí)行任務(wù)需要的參數(shù)。

注意注意,這里我們?yōu)榱艘子诶斫?,只拿constant舉例子,以后慢慢復(fù)雜起來,會越來越豐富。

注意注意,module發(fā)布了任務(wù)以后,只是發(fā)布了,并沒有執(zhí)行。

 

那么什么時候執(zhí)行呢?

當(dāng)angular一個app啟動的時候,會自動生成一個injector,也就是大家口中的注射器,這是一個對象,這個injector對象會讀取module中的各種任務(wù)。

比如injecotr讀取module的invokeQueue之后,發(fā)現(xiàn)了第一條任務(wù):

1
['constant',['name','不咬人的蚊子']]

于是injector就會發(fā)現(xiàn),這是一個constant任務(wù),參數(shù)是name,'不咬人的蚊子'。

injector并不能處理constant任務(wù),所以它去找一個名為constant的provider,這個provider可以提供一個函數(shù),這個函數(shù)正好接收兩個參數(shù)。

于是injector把任務(wù)中的兩個參數(shù)(也就是name和'不咬人的蚊子'這兩個參數(shù))交給constantProvider,讓它來執(zhí)行。

 

好了,這就是一個口頭能講明白的原理。那么angularJs是如何實現(xiàn)這個機制的呢?我打算把簡單版的代碼貼在下面,如果感興趣的同學(xué)可以看看,如果不感興趣的同學(xué)其實只要把上面的文字給看明白了,下面的代碼隨便看個樂呵就行。(這個代碼可能會有部分是接著上一篇博客的代碼,如果看著不知道怎么回事,可以看看上一篇博客。)

 

setupModuleLoader.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function setupModuleLoader(window){
    var ensure=function(obj,name,factory){
        return obj[name]||(obj[name]=factory())
    }
    var angular = ensure(window,'angular',Object);
 
    var createModule = function(name,requires){
        var invokeQueue=[];//增加一個任務(wù)隊列
        var moduleInstance = {
            name:name,
            requires:requires,
            _invokeQueue:invokeQueue,
                        //constant方法的實質(zhì)是向invokeQueue數(shù)組里面增加一個任務(wù)
            constant:function(key,value){
                invokeQueue.push(['constant',[key,value]])
            },
        };
        return moduleInstance;
    }
 
    ensure(angular,'module',function(){
        var modules={};
        return function(name,requires){
            if(requires){
                return createModule(name,requires,modules)
            }else{
                return getModule(name,modules);
            }
        }
    })
}

createInjector.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//createInjector(['app1','app2'])
//參數(shù)是一個字符串或者一個數(shù)組,內(nèi)容是module名
function createInjector(modulesToLoad){
    //cache用來緩存一些一直可以用到的值
    var cache={};
 
    $provide={
        constant:function(key,value){
            cache[key]=value;
        }
    }
 
    //這里的foreach方法并不是一個真正能運行的foreach,能看懂就行了
    //每次APP啟動的時候,injector都會按照傳入的module名來遍歷所有module
    //這樣就可以得到所有module發(fā)布的任務(wù),并且一一執(zhí)行這些任務(wù)
    $.forEach(modulesToLoad,function(moduleName){
        var module = window.angular.module(moduleName);
        $.forEach(module._invokeQueue,function(invokeArgs){
            var method=invokeArgs[0];
            var args = invokeArgs[1];
            $provide[method].apply($provide,args);
        })
    })
    return {
        has:function(key){
            return cache.hasOwnProperty(key)
        },
        get:function(key){
            return cache[key]
        }
    }
}

 

如果你耐著性子看到了這里,并且思路還算清晰,那么你肯定會問,現(xiàn)在injector執(zhí)行了所有任務(wù),并且把一切東西都準備好了,那么我們使用這些數(shù)據(jù)的途徑和方法是什么呢?哈哈,這個別急,很快會解釋明白,但是現(xiàn)在起碼我們對依賴注入有了一個很好的理解了不是么?冬天來了,春天不會遠了。