AngularJS 源码分析1

2020-12-13 05:18

阅读:605

标签:style   blog   class   code   c   tar   

AngularJS简介

angularjs 是google出品的一款MVVM前端框架,包含一个精简的类jquery库,创新的开发了以指令的方式来组件化前端开发,可以去它的官网看看,请戳这里

再贴上一个本文源码分析对应的angularjs源码合并版本1.2.4,精简版的,除掉了所有的注释, 请戳这里

从启动开始说起

定位到4939行,这里是angularjs开始执行初始化的地方,见代码

?
1
2
3
bindJQuery(), publishExternalAPI(angular), jqLite(document).ready(function() {
        angularInit(document, bootstrap)
    })

bindJQuery方法是检查是否引用jquery,没有的话jqlite就用本身自带的,否则切换到jquery中去.这个好理解

publishExternalAPI这个方法是绑定一些公共的方法到angular下面,这些是可以在网站中访问到的,像forEach,copy等公共方法,还有一个重要的任务就是初始化angular核心的模块,publishExternalAPI在465行,现在我们来分析里面的一些重要的代码

?
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
function publishExternalAPI(angular) {
        extend(angular, {
            bootstrap: bootstrap,
            copy: copy,
            extend: extend,
            equals: equals,
            element: jqLite,
            forEach: forEach,
            injector: createInjector,
            noop: noop,
            bind: bind,
            toJson: toJson,
            fromJson: fromJson,
            identity: identity,
            isUndefined: isUndefined,
            isDefined: isDefined,
            isString: isString,
            isFunction: isFunction,
            isObject: isObject,
            isNumber: isNumber,
            isElement: isElement,
            isArray: isArray,
            version: version,
            isDate: isDate,
            lowercase: lowercase,
            uppercase: uppercase,
            callbacks: {
                counter: 0
            },
            $$minErr: minErr,
            $$csp: csp
        }), angularModule = setupModuleLoader(window);
        try {
            angularModule("ngLocale")
        } catch (e) {
            angularModule("ngLocale", []).provider("$locale", $LocaleProvider)
        }
        angularModule("ng", ["ngLocale"], ["$provide",
            function($provide) {
                $provide.provider("$compile", $CompileProvider).directive({
                    a: htmlAnchorDirective,
                    input: inputDirective,
                    textarea: inputDirective,
                    form: formDirective,
                    script: scriptDirective,
                    select: selectDirective,
                    style: styleDirective,
                    option: optionDirective,
                    ngBind: ngBindDirective,
                    ngBindHtml: ngBindHtmlDirective,
                    ngBindTemplate: ngBindTemplateDirective,
                    ngClass: ngClassDirective,
                    ngClassEven: ngClassEvenDirective,
                    ngClassOdd: ngClassOddDirective,
                    ngCloak: ngCloakDirective,
                    ngController: ngControllerDirective,
                    ngForm: ngFormDirective,
                    ngHide: ngHideDirective,
                    ngIf: ngIfDirective,
                    ngInclude: ngIncludeDirective,
                    ngInit: ngInitDirective,
                    ngNonBindable: ngNonBindableDirective,
                    ngPluralize: ngPluralizeDirective,
                    ngRepeat: ngRepeatDirective,
                    ngShow: ngShowDirective,
                    ngStyle: ngStyleDirective,
                    ngSwitch: ngSwitchDirective,
                    ngSwitchWhen: ngSwitchWhenDirective,
                    ngSwitchDefault: ngSwitchDefaultDirective,
                    ngOptions: ngOptionsDirective,
                    ngTransclude: ngTranscludeDirective,
                    ngModel: ngModelDirective,
                    ngList: ngListDirective,
                    ngChange: ngChangeDirective,
                    required: requiredDirective,
                    ngRequired: requiredDirective,
                    ngValue: ngValueDirective
                }).directive(ngAttributeAliasDirectives).directive(ngEventDirectives), $provide.provider({
                    $anchorScroll: $AnchorScrollProvider,
                    $animate: $AnimateProvider,
                    $browser: $BrowserProvider,
                    $cacheFactory: $CacheFactoryProvider,
                    $controller: $ControllerProvider,
                    $document: $DocumentProvider,
                    $exceptionHandler: $ExceptionHandlerProvider,
                    $filter: $FilterProvider,
                    $interpolate: $InterpolateProvider,
                    $interval: $IntervalProvider,
                    $http: $HttpProvider,
                    $httpBackend: $HttpBackendProvider,
                    $location: $LocationProvider,
                    $log: $LogProvider,
                    $parse: $ParseProvider,
                    $rootScope: $RootScopeProvider,
                    $q: $QProvider,
                    $sce: $SceProvider,
                    $sceDelegate: $SceDelegateProvider,
                    $sniffer: $SnifferProvider,
                    $templateCache: $TemplateCacheProvider,
                    $timeout: $TimeoutProvider,
                    $window: $WindowProvider
                })
            }
        ])
    }

方法体中的setupModuleLoader方法是一个模块加载器,这也是一个关键方法, 主要作用是创建和获取模块,代码见417行.

?
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
function setupModuleLoader(window) {
 
        function ensure(obj, name, factory) {
            return obj[name] || (obj[name] = factory())
        }
 
        var $injectorMinErr = minErr("$injector"),
            ngMinErr = minErr("ng");
 
        return ensure(ensure(window, "angular", Object), "module", function() {
            var modules = {};
 
            return function(name, requires, configFn) {
                var assertNotHasOwnProperty = function(name, context) {
                    if ("hasOwnProperty" === name) throw ngMinErr("badname", "hasOwnProperty is not a valid {0} name", context)
                };
                return assertNotHasOwnProperty(name, "module"), requires && modules.hasOwnProperty(name) && (modules[name] = null), ensure(modules, name, function() {
                    function invokeLater(provider, method, insertMethod) {
                        return function() {
                            return invokeQueue[insertMethod || "push"]([provider, method, arguments]), moduleInstance
                        }
                    }
                    if (!requires) throw $injectorMinErr("nomod", "Module ‘{0}‘ is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.", name);
                    var invokeQueue = [],
                        runBlocks = [],
                        config = invokeLater("$injector", "invoke"),
                        moduleInstance = {
                            _invokeQueue: invokeQueue,
                            _runBlocks: runBlocks,
                            requires: requires,
                            name: name,
                            provider: invokeLater("$provide", "provider"),
                            factory: invokeLater("$provide", "factory"),
                            service: invokeLater("$provide", "service"),
                            value: invokeLater("$provide", "value"),
                            constant: invokeLater("$provide", "constant", "unshift"),
                            animation: invokeLater("$animateProvider", "register"),
                            filter: invokeLater("$filterProvider", "register"),
                            controller: invokeLater("$controllerProvider", "register"),
                            directive: invokeLater("$compileProvider", "directive"),
                            config: config,
                            run: function(block) {
                                return runBlocks.push(block), this
                            }
                        };
                    return configFn && config(configFn), moduleInstance
                })
            }
        })
    }

上面publishExternalAPI 方法中的angularModule = setupModuleLoader(window);是在window下面创建全局的angular对象,并且返回一个高阶函数,赋值给了angular.module属性,所以一般我们创建模块都是用angular.module方法.这里的angularModule其实就是相当于angular.module

angular.module在创建模块的时候,传递一个参数的时候,是获取模块;传递一个以上的是创建新模块;该方法返回的是一个moduleInstance对象,它的任务就是来创建控制器,服务,指令,以及配置方法,全局运行方法,而且是链式调用,因为每个方法都会返回moduleInstance,看这里

?
1
2
3
4
5
function invokeLater(provider, method, insertMethod) {
    return function() {
        return invokeQueue[insertMethod || "push"]([provider, method, arguments]), moduleInstance
    }
}

此处的return invokeQueue[insertMethod || "push"]([provider, method, arguments]), moduleInstance,逗号表达式是返回最后一个值

再来一个angular.module在项目中运用的代码

?
1
2
3
4
5
6
angular.module(‘demoApp‘, [])
.factory()
.controller()
.directive()
.config()
.run();

接下来再看publishExternalAPI的代码,因为ngLocale默认没有创建,所以angularModule("ngLocale")这个直接异常,跳到catch里执行angularModule("ngLocale", []).provider("$locale", $LocaleProvider),记住这里的provider方法,默认是把它的参数都存到invokeQueue数组中,以便在后面用到.

接下来开始创建ng模块,它依赖上面的ngLocale模块,注意创建模块的时候传了第三个参数,当创建模块的时候传了三个参数,默认第三参数会执行config(configFn),这个方法也是把相应的参数放入invokeQueue数组中,只不过前两参数是$injector,invoke,这里先透露一下,其实所有invokeQueue数组项中,三个参数的意思:第一个参数调用第二个参数,然后传递第三个参数,这个后面会讲到.

这里说下ng模块中第三个参数里的函数体,这里主要做了两件事,初始了$compile服务,并且利用compile服务的directive方法,把一些常用的指令都保存到compile服务中的一个内部数组中.

这里先说下$provide.provider,这个在angular里用的比较多,其实就是提前把定义的provider放入DI函数内的providerCache内,看如下代码,在740行

?
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
function createInjector(modulesToLoad) {
        function supportObject(delegate) {
            return function(key, value) {
                return isObject(key) ? (forEach(key, reverseParams(delegate)), void 0) : delegate(key, value)
            }
        }
 
        function provider(name, provider_) {
            if (assertNotHasOwnProperty(name, "service"), (isFunction(provider_) || isArray(provider_)) && (provider_ = providerInjector.instantiate(provider_)), !provider_.$get) throw $injectorMinErr("pget", "Provider ‘{0}‘ must define $get factory method.", name);
            return providerCache[name + providerSuffix] = provider_
        }
 
        function factory(name, factoryFn) {
            return provider(name, {
                $get: factoryFn
            })
        }
 
        function service(name, constructor) {
            return factory(name, ["$injector",
                function($injector) {
                    return $injector.instantiate(constructor)
                }
            ])
        }
 
        function value(name, val) {
            return factory(name, valueFn(val))
        }
 
        function constant(name, value) {
            assertNotHasOwnProperty(name, "constant"), providerCache[name] = value, instanceCache[name] = value
        }
 
        function decorator(serviceName, decorFn) {
            var origProvider = providerInjector.get(serviceName + providerSuffix),
                orig$get = origProvider.$get;
            origProvider.$get = function() {
                var origInstance = instanceInjector.invoke(orig$get, origProvider);
                return instanceInjector.invoke(decorFn, null, {
                    $delegate: origInstance
                })
            }
        }
 
        function loadModules(modulesToLoad) {
            var moduleFn, invokeQueue, i, ii, runBlocks = [];
            return forEach(modulesToLoad, function(module) {
                if (!loadedModules.get(module)) {
                    loadedModules.put(module, !0);
                    try {
                        if (isString(module))
                            for (moduleFn = angularModule(module), runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks), invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; ii > i; i++) {
                                var invokeArgs = invokeQueue[i],
                                    provider = providerInjector.get(invokeArgs[0]);
                                provider[invokeArgs[1]].apply(provider, invokeArgs[2])
                            } else isFunction(module) ? runBlocks.push(providerInjector.invoke(module)) : isArray(module) ? runBlocks.push(providerInjector.invoke(module)) : assertArgFn(module, "module")
                    } catch (e) {
                        throw isArray(module) && (module = module[module.length - 1]), e.message && e.stack && -1 == e.stack.indexOf(e.message) && (e = e.message + "\n" + e.stack), $injectorMinErr("modulerr", "Failed to instantiate module {0} due to:\n{1}", module, e.stack || e.message || e)
                    }
                }
            }),runBlocks
        }
 
        function createInternalInjector(cache, factory) {
            function getService(serviceName) {
                if (cache.hasOwnProperty(serviceName)) {
                    if (cache[serviceName] === INSTANTIATING) throw $injectorMinErr("cdep", "Circular dependency found: {0}", path.join(" ));
                    return cache[serviceName]
                }
                try {
                    return path.unshift(serviceName), cache[serviceName] = INSTANTIATING, cache[serviceName] = factory(serviceName)
                } finally {
                    path.shift()
                }
            }
 
            function invoke(fn, self, locals) {
                var length, i, key, args = [],
                    $inject = annotate(fn);
                for (i = 0, length = $inject.length; length > i; i++) {
                    if (key = $inject[i], "string" != typeof key) throw $injectorMinErr("itkn", "Incorrect injection token! Expected service name as string, got {0}", key);
                    args.push(locals && locals.hasOwnProperty(key) ? locals[key] : getService(key))
                }
                switch (fn.$inject || (fn = fn[length]), self ? -1 : args.length) {
                    case 0:
                        return fn();
                    case 1:
                        return fn(args[0]);
                    case 2:
                        return fn(args[0], args[1]);
                    case 3:
                        return fn(args[0], args[1], args[2]);
                    case 4:
                        return fn(args[0], args[1], args[2], args[3]);
                    case 5:
                        return fn(args[0], args[1], args[2], args[3], args[4]);
                    case 6:
                        return fn(args[0], args[1], args[2], args[3], args[4], args[5]);
                    case 7:
                        return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
                    case 8:
                        return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
                    case 9:
                        return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
                    case 10:
                        return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]);
                    default:
                        return fn.apply(self, args)
                }
            }
 
            function instantiate(Type, locals) {
                var instance, returnedValue, Constructor = function() {};
                return Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype, instance = new Constructor, returnedValue = invoke(Type, instance, locals), isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance
            }
            return {
                invoke: invoke,
                instantiate: instantiate,
                get: getService,
                annotate: annotate,
                has: function(name) {
                    return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name)
                }
            }
        }
        &gt


评论


亲,登录后才可以留言!