Angularjs的按需加载(2)
上一篇我们实现了最简单的Angularjs的按需加载,
可以通过替换Angular的内部方法,再使用require.js配合$q完成了按需加载.
今天来说说Angular如何按需加载第三方module;
在此之前,我们得先弄清楚Angular是如何启动的.
setupModuleLoader方法
在上图中,我们得注意一个非常主要的方法;
setupModuleLoader方法,顾名思义 —— 模块加载器;
此方法最后返回的对象为:
var 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) {
runBlocks.push(block);
return this;
}
}
看着这对象的格式,有没有特别熟悉?
当我们运行
angular.module('app',[]);
//或者
angular.module('app')
我们新建一个module或者获取一个module的时候, 返回的就是这个对象.
当我们做链式操作的时候, angular.module(‘app’,[]).config().run().controller();
每一步操作,返回的都是它,所以我们才可以做链式操作;
当angular初始化完成之后,开始在dom里寻找ng-app属性;
当找到ng-app后,拿到ng-app的value后才开始启动 angular; 内部运行bootstrap方法;
如果要选择手动启动的话也可以这样:
angular.bootstrap(document, ['moduleName']);
当bootstrap的时候angular在做什么呢?
doBootstrap方法
在源码里中的bootstrap方法里有一个doBootstrap方法:
var doBootstrap = function() {
element = jqLite(element);
//判断是否启动
if (element.injector()) {
var tag = (element[0] === document) ? 'document' : startingTag(element);
throw ngMinErr(
'btstrpd',
"App Already Bootstrapped with this Element '{0}'",
tag.replace(/</,'<').replace(/>/,'>'));
}
//angular.bootstrap(document, ['moduleName']);
//这里的modules就是之前我们bootstrap时候传进来的 moduleName
//modules ==> ['moduleName']
modules = modules || [];
modules.unshift(['$provide', function($provide) {
$provide.value('$rootElement', element);
}]);
.....
//重点来了 createInjector
var injector = createInjector(modules, config.strictDi);
injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
function bootstrapApply(scope, element, compile, injector) {
scope.$apply(function() {
element.data('$injector', injector);
compile(element)(scope);
});
}]
);
return injector;
};
createInjector方法
我们看这个方法主要做了什么
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
}
},
providerInjector = (providerCache.$injector =
createInternalInjector(providerCache, function(serviceName, caller) {
if (angular.isString(caller)) {
path.push(caller);
}
throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
})),
instanceCache = {},
protoInstanceInjector =
createInternalInjector(instanceCache, function(serviceName, caller) {
var provider = providerInjector.get(serviceName + providerSuffix, caller);
return instanceInjector.invoke(
provider.$get, provider, undefined, serviceName);
}),
instanceInjector = protoInstanceInjector;
providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
//重点!!! 这里的modulesToLoad 就是我们之前传进来的参数
//modulesToLoad ==> ['moduleName']
var runBlocks = loadModules(modulesToLoad);
instanceInjector = protoInstanceInjector.get('$injector');
instanceInjector.strictDi = strictDi;
forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); });
return instanceInjector;
...
//声明的一些函数
}
loadModules方法
function loadModules(modulesToLoad) {
//判断modulesToLoad 传进来的是不是一个数组
//assertArg函数是报错用的
assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
var runBlocks = [], moduleFn;
//开始遍历
forEach(modulesToLoad, function(module) {
if (loadedModules.get(module)) return;
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 {
//现在我们拿到了我们的moduleName
if (isString(module)) {
//angularModule其实就是之前的setupModuleLoader方法,
//那当然他返回的就是moduleInstance对象,也就是说的那个
moduleFn = angularModule(module);
//把模块里的runBlocks都取出来
runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
//然后开始调用上面的方法
//开始通过runInvokeQueue方法遍历执行 之前setupModuleLoader实例化的方法
runInvokeQueue(moduleFn._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) {
....
//一些对于异常的处理
}
});
return runBlocks;
}
以上就是angular加载模块的主要方法
微信公众号
