花了点时间,阅读了下 angularjs 的源码。本次先从模块化开始。
angular 可以通过 module 的 api 来实现前端代码的模块化管理。跟 define 类似。但不具备异步加载脚本的功能。先从最基本的 module 开始。查看代码发现 angular 对 module 的定义是在 setupModuleLoader 方法中定义的
- function setupModuleLoader(window) {
- var $injectorMinErr = minErr('$injector');
- var ngMinErr = minErr('ng');
- function ensure(obj, name, factory) {
- return obj[name] || (obj[name] = factory());
- }
- var angular = ensure(window, 'angular', Object);
- // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
- angular.$$minErr = angular.$$minErr || minErr;
- return ensure(angular, 'module',
- function() {
- return function module(name, requires, configFn) {
- //省略.....
- return ensure(modules, name,
- function() { //省略...
- return moduleInstance;
- });
- };
- });
- }
可以看到源码中是通过 ensure 函数,将 module 定义到 angular,并且在函数内返回函数,这就使用到了闭包,angular 的目的是为了存储对应的 module 信息。在整个 angular 模块系统中,源码通过 modules 来存储所有用户定义的模块。当用户对自己定义的模块注册服务时 (factory,service,provider,value 时),这些服务会被存在各自模块内部的 invokeQueue 数组中维护。这同样是一个闭包。
当 setupModuleLoader 方法执行结束之后,返回的值是一个 moduleInstance。这也是我们在外部调用 angular.module 时返回的值,看一下内部结构:
- var moduleInstance = {
- // Private state
- _invokeQueue: invokeQueue,
- _configBlocks: configBlocks,
- _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) {
- runBlocks.push(block);
- return this;
- }
- };
我们可以使用_invokeQueue 属性查看,当前 module 下面注册了哪些服务。另外 moduleInstance 也向外部公开了诸如 provider,factory.service 这些接口,供用户去注册自定义服务。至于如何注册的,是通过其内部的 invokeLater 方法,统统加入到 invokeQueue 数组中
- function invokeLater(provider, method, insertMethod, queue) {
- if (!queue) queue = invokeQueue;
- return function() {
- queue[insertMethod || 'push']([provider, method, arguments]);
- return moduleInstance;
- };
- }
目前,我们大致了解了模块的定义和一般服务的注册。大致调用代码如下:
- var app = angular.module("A", []); //定义一个模块A
- app.service("A1",
- function() {
- console.log(1);
- }); //在模块A上定义一个服务A1
ok,万事俱备,只欠业炎。我们如何使用它?angular 提供了一个 injector 的方法,我们来看一下。从内部函数 publishExternalAPI 中看到 injector 的定义,它来源于 angular 的一个叫做 createInInjector 的函数。
- function createInjector(modulesToLoad, strictDi) {
- strictDi = (strictDi === true);
- var INSTANTIATING = {},
- providerSuffix = 'Provider',
- path = [],
- loadedModules = new HashMap([], true),
- providerCache = {
- $provide: {
- provider: supportObject(provider),
- factory: supportObject(factory),
- service: supportObject(service),
- value: supportObject(value),
- constant: supportObject(constant),
- decorator: decorator
- }
- };
- var providerInjector = (providerCache.$injector = createInternalInjector(providerCache,
- function(serviceName, caller) {
- if (angular.isString(caller)) {
- path.push(caller);
- }
- throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
- })) var instanceCache = {};
- var instanceInjector = (instanceCache.$injector = createInternalInjector(instanceCache,
- function(serviceName, caller) {
- var provider = providerInjector.get(serviceName + providerSuffix, caller);
- return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
- }));
- forEach(loadModules(modulesToLoad),
- function(fn) {
- instanceInjector.invoke(fn || noop);
- });
- return instanceInjector;
- // 省略.....
- }
可以看出,内部定义了两个核心对象 providerCache 和 instanceCache。它们都是由 createInternalInjector 函数生成。那进入 createInternalInjector 函数里看一下
- function createInternalInjector(cache, factory) {
- //....
- return {
- invoke: invoke,
- instantiate: instantiate,
- get: getService,
- annotate: createInjector.$$annotate,
- has: function(name) {
- //对象本身是否有该属性
- return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
- }
- };
- }
因为 createInjector 函数中最终返回的是 instanceInjector,而此君指向的则是 instanceCache 的 $injector 属性,而这个属性则是 createInternalInjector 函数的返回值。换言之,用户可以在外部调用这些方法
- var injector = angular.injector(["A"]);
- //其中injector就是createInternalInjector函数的返回的对象
在我们调用这些接口之前,需要告诉 angular 去执行哪个模块里的服务,让我们回到 createInjector 函数中,看一下函数最后的 for 循环
- forEach(loadModules(modulesToLoad),
- function(fn) {
- instanceInjector.invoke(fn || noop);
- });
可以猜想这个 loadModules 函数的作用应该是获取模块。简单看一下 loadModules
- function loadModules(modulesToLoad) {
- var runBlocks = [],
- moduleFn;
- forEach(modulesToLoad,
- function(module) {
- if (loadedModules.get(module)) return; //先尝试去loadedModules查找
- loadedModules.put(module, true);
- function runInvokeQueue(queue) {
- var i, ii;
- for (i = 0, ii = queue.length; i < ii; i++) {
- //获取用户之前需要注册的服务
- var invokeArgs = queue[i],
- provider = providerInjector.get(invokeArgs[0]);
- provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
- }
- }
- try {
- if (isString(module)) {
- moduleFn = angularModule(module); //angularModule其实就是外部用户调用的module接口,获取到对应模块
- runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); //如果模块有依赖则事先获取依赖模块
- runInvokeQueue(moduleFn._invokeQueue); //之前所有的声明的服务都被存在放这个模块的invokeQueue数组中
- runInvokeQueue(moduleFn._configBlocks);
- } else if (isFunction(module)) {
- runBlocks.push(providerInjector.invoke(module));
- } else if (isArray(module)) {
- runBlocks.push(providerInjector.invoke(module));
- } else {
- assertArgFn(module, 'module');
- }
- } catch(e) {
- if (isArray(module)) {
- module = module[module.length - 1];
- }
- if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
- // Safari & FF's stack traces don't contain error.message content
- // unlike those of Chrome and IE
- // So if stack doesn't contain message, we create a new string that contains both.
- // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
- /* jshint -W022 */
- e = e.message + '\n' + e.stack;
- }
- throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}", module, e.stack || e.message || e);
- }
- });
- return runBlocks;
- }
在 runInvokeQueue 中,我们看到源码获取了之前用户存在放 invokeQueue 中的服务对象,并且通过 get 方法及 getService 方法去获取服务。这里有个细节地方,源码是通过 providerInjector 的 get 方法来获取的,那我们看 getService 源码的时候,其中的 cache 则应该是在之前 createInjector 函数中的 providerCache。
- function getService(serviceName, caller) {
- if (cache.hasOwnProperty(serviceName)) {
- if (cache[serviceName] === INSTANTIATING) {
- throw $injectorMinErr('cdep', 'Circular dependency found: {0}', serviceName + ' <- ' + path.join(' <- '));
- }
- return cache[serviceName];
- } else {
- try {
- path.unshift(serviceName);
- cache[serviceName] = INSTANTIATING;
- return cache[serviceName] = factory(serviceName, caller);
- } catch(err) {
- if (cache[serviceName] === INSTANTIATING) {
- delete cache[serviceName];
- }
- throw err;
- } finally {
- path.shift();
- }
- }
- }
基本还是先尝试在 cache 中找一次,没有则创建并保存在 cache 中。因为之前 providerInjector 中就已经有了 $provider 何 $inject 这样的方法。所以在
- var invokeArgs = queue[i],
- provider = providerInjector.get(invokeArgs[0]);
- provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
中,provider 就是:
- providerCache = {
- $provide: {
- provider: supportObject(provider),
- factory: supportObject(factory),
- service: supportObject(service),
- value: supportObject(value),
- constant: supportObject(constant),
- decorator: decorator
- }
- };
然后,再有后面的一步 provider[invokeArgs[1].apply(provider, invokeArgs[2])]。看到这里我们突然恍然大悟,开始 angular 为什么要使用
- 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'),
这类写法注册的原因了,也就是现在我们才真正调用具体 service,factory 或者是 value 等 api 去执行或者讲要初始化我们自定义的服务。这里我们顺便看一下 factory,service 和 provider 的内部实现,这些 API 以后我们会经常用到:
- function provider(name, provider_) {
- assertNotHasOwnProperty(name, 'service');
- if (isFunction(provider_) || isArray(provider_)) {
- provider_ = providerInjector.instantiate(provider_); //实例化
- }
- if (!provider_.$get) {
- throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
- }
- return providerCache[name + providerSuffix] = provider_; //并存储在本地缓存中
- }
- function factory(name, factoryFn, enforce) {
- return provider(name, { //底层调用的是provider
- $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
- });
- }
- function service(name, constructor) {
- return factory(name, ['$injector',
- function($injector) { //底层调用factory
- return $injector.instantiate(constructor);
- }]);
- }
- function value(name, val) {
- return factory(name, valueFn(val), false);
- } //底层调用factory
factory,service 和 value 基本底层都是使用的 provider 方法。可以看到 provider 方法需要 $get 的这种键值对的方式将函数传入。所以我们在外部使用这一系列 API 的方式,大致
- var app = angular.module("A", []);
- app.service("A1",
- function() {
- console.log(1);
- });
- app.factory("B1", [function() {
- console.log(2);
- return {};
- }]);
- app.provider("C1", {
- $get: function() {
- console.log(3);
- }
- });
- app.value("D1", 4);
- app.constant("E1", 5);
有兴趣的朋友可以试下,service 使用 factory 的方法定义可以不可以 ^_^。至此,我们之前使用 injector() 方法实际上获取到了 instanceInjector。目前我们还是没有触发或者执行我们定义的服务,实际上 instanceInjector 上提供了如下几个方法:
- return {
- invoke: invoke,
- instantiate: instantiate,
- get: getService,
- annotate: createInjector.$$annotate,
- has: function(name) {
- //对象本身是否有该属性
- return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
- }
- };
当我们执行 get 方法的时候,我们注册的服务的以执行!
- function getService(serviceName, caller) {
- if (cache.hasOwnProperty(serviceName)) {
- if (cache[serviceName] === INSTANTIATING) {
- throw $injectorMinErr('cdep', 'Circular dependency found: {0}', serviceName + ' <- ' + path.join(' <- '));
- }
- return cache[serviceName];
- } else {
- try {
- path.unshift(serviceName);
- cache[serviceName] = INSTANTIATING;
- return cache[serviceName] = factory(serviceName, caller);
- } catch(err) {
- if (cache[serviceName] === INSTANTIATING) {
- delete cache[serviceName];
- }
- throw err;
- } finally {
- path.shift();
- }
- }
- }
这里你可能会奇怪这个 factory 到底是哪个?是来自于 providerInjector 的还是来自 instanceInjector 的?实际上 factory 是由 instanceInjector 中定义的回调函数。为什么呢?因为上文我们说了外部调用 injector 的时候 angular 内部返回的是 instanceInjector。所以在 instanceInjector 上调用 get 时,这个 factory 应该就是 instanceInjector 上注册的回调函数,来看一下这个回调:
- function(serviceName, caller) {
- var provider = providerInjector.get(serviceName + providerSuffix, caller);
- return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
- }
在 instanceInjector 的回调里面调用 providerInjector 的 get,其实这个很清楚,之前 angular 将所有服务存储都存放在了 providerCache 里面,而外部对用户而言,他无法直接修改 providerCache 里面的东西。重复的 get 我们就不看,来看一下 invoke 方法:
- function invoke(fn, self, locals, serviceName) {
- if (typeof locals === 'string') {
- serviceName = locals;
- locals = null;
- }
- var args = [],
- $inject = createInjector.$$annotate(fn, strictDi, serviceName),
- length,
- i,
- key;
- for (i = 0, length = $inject.length; i < length; i++) {
- key = $inject[i];
- if (typeof key !== 'string') {
- throw $injectorMinErr('itkn', 'Incorrect injection token! Expected service name as string, got {0}', key);
- }
- args.push(locals && locals.hasOwnProperty(key) ? locals[key] : getService(key, serviceName));
- }
- if (isArray(fn)) {
- fn = fn[length];
- }
- // http://jsperf.com/angularjs-invoke-apply-vs-switch
- // #5388
- return fn.apply(self, args); //执行服务
- }
千呼万唤始出来啊。服务的执行在 invoke 方法得到执行,那么我们算是基本的将 module 和服务注册和执行的流程简单的过了一遍。
简单回顾一下,angular 的 module 实际上就是用了两层闭包来管理对象,没有动态异步加载模块,这个功能有点类似 namespace。比较鸡肋。另外返回的 moduleInstance 中有 factory,service,provider,value 等一些可以注册服务的接口,其内部使用了 providerInjector 何 instanceInjector 来存储相关服务。通过 invoke 来执行相应模块里的相应服务。目前我们还没有看 angular 的 bootstrap 部分,其内部也是调用 invoke 方法,达到内置注册的服务得到执行。
时间不多,内容刚好,以上是个人阅读源码的一些理解,有不对或者偏差的地方,还希望园友们斧正。共同进步。
来源: http://www.cnblogs.com/wumadi/p/6616778.html