跟我读AngularJs的源代码
你要相信,再牛逼的框架他的本质也是javascript代码,只要你会js代码你就能看懂!别怂!
我这里分析的是 我们来分析一下angular的核心文件(),打开 ()();//一个简单的js闭包稍微复杂一点:(function(“参数”){})(“参数”);//所有的代码放在了这匿名function中
可以看到大概代码量3万多行,是不是感觉有点崩溃,看起来还是有点费劲的。不用担心,你只要拉到js文件最底部,这里是初始化时调用的几个函数,从这里入口,你会很顺畅的理顺整个 在最底部,31310行代码处: 判断angular是否启动(初始化)过了,如果初始化过就提示错误,没有则return; 然后是绑定 //try to bind to jquery now so that one can write jqLite(document).ready()
具体的实现你可以点击方法看一下。就是如果你引入了jQuery库就调用你引用的,没有引用则使用angular内部的一个轻量级的jQuery(jqLite)。 原文注释: 接下来就是初始化 最后教你一个最简单的方式,因为js是解释型的弱类型语言,也可以说是很没有原则的语言,怎么玩都行,你可以把任意的js对象打印到浏览器页面,利用浏览器的开发者选项查看该js对象的属性,样貌。比单纯的看代码强多了。 用浏览器把angular对象打印出来是这样的: 我这里用的是google的chrome浏览器,可以看到angular对象的每一个熟悉,然后你想了解哪个属性点击它展开就行了。是不是很方便快捷。 总之你记住一句话,根本没有什么难得,难者不会,会者不难,互联网开发者,全球都是平等的,只要你想,你就可以做到跟google的工程师一样优秀! angularjs源码分析之:angularjs执行流程
angularjs源码分析之:angularjs执行流程先上个大图,有个大概印象,注:angularjs的版本为:1.2.1,通过 几个重要方法bindJQuery();
publishExternalAPI(angular);
jqLite(document).ready(function() {
angularInit(document,bootstrap);
});
20121行,bindJQuery,尝试绑定jQuery对象,如果没有则采用内置的jqLite 1850行, angular.module = function module(name,require,configFn);
当我们 当我们 var moduleInstance = {
// Private state
_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',controller: invokeLater('$controllerProvider',directive: invokeLater('$compileProvider','directive'),config: config,run: function(block) {
runBlocks.push(block);
return this;
}
}
因为这样,我们才可以链式操作 ,如: 这里需要重点提到两个函数: 通过 此机制完成了在 通过
20125行,domready后调用angularInit function angularInit(element,bootstrap) {
var elements = [element],appElement,module,names = ['ng:app','ng-app','x-ng-app','data-ng-app'],NG_APP_CLASS_REGEXP = /sng[:-]app(:s*([wd_]+);?)?s/;
function append(element) {
element && elements.push(element);
}
forEach(names,function(name) {
names[name] = true;
append(document.getElementById(name));
name = name.replace(':',':');
if (element.querySelectorAll) {
forEach(element.querySelectorAll('.' + name),append);
forEach(element.querySelectorAll('.' + name + ':'),append);
forEach(element.querySelectorAll('[' + name + ']'),append);
}
});
forEach(elements,function(element) {
if (!appElement) {
var className = ' ' + element.className + ' ';
var match = NG_APP_CLASS_REGEXP.exec(className);
if (match) {
appElement = element;
module = (match[2] || '').replace(/s+/g,',');
} else {
forEach(element.attributes,function(attr) {
if (!appElement && names[attr.name]) {
appElement = element;
module = attr.value;
}
});
}
}
});
if (appElement) {
bootstrap(appElement,module ? [module] : []);
}
}
遍历names,通过 bootstrap中需要重点关注 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);
}
//通过上面分析我们知道此时 modules 暂时是这样的: modules = ['myApp'];
modules = modules || [];
//添加$provide这个数组
modules.unshift(['$provide',function($provide) {
$provide.value('$rootElement',element);
}]);
//添加 ng这个 module,注意:1857行 我们注册过ng 这个module,并在1854行 我们注册过 它的依赖模块'ngLocale',
//angularModule('ngLocale',[]).provider('$locale',$LocaleProvider); 我们注册过ngLocale这个module
modules.unshift('ng');
//调用createInjector(module) 此时:module为:
//['ng',['$provide',function(){}],'myApp'] 两个type为string,一个为array
var injector = createInjector(modules);
injector.invoke(['$rootScope','$rootElement','$compile','$injector','$animate',function(scope,element,compile,injector,animate) {
scope.$apply(function() {
element.data('$injector',injector);
compile(element)(scope);
});
}]
);
return injector;
};
createInjector是重点,拿出来单独分析 function createInjector(modulesToLoad) {
var INSTANTIATING = {},providerSuffix = 'Provider',path = [],loadedModules = new HashMap(),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() {
throw $injectorMinErr('unpr',"Unknown provider: {0}",path.join(' <- '));
})),instanceCache = {},instanceInjector = (instanceCache.$injector =
createInternalInjector(instanceCache,function(servicename) {
var provider = providerInjector.get(servicename + providerSuffix);
return instanceInjector.invoke(provider.$get,provider);
}));
forEach(loadModules(modulesToLoad),function(fn) { instanceInjector.invoke(fn || noop); });
return instanceInjector;
/** ...省略若干 **/
主要是四个变量:
providerCache = {
$provide: {
provider: supportObject(provider),value: supportObject(value),constant: supportObject(constant),$injector:{
get:getService,annotate:annotate,instantiate:instantiate,invoke:invoke,has:has
}
}
而 providerInjector:{ nvoke: invoke,instantiate: instantiate,get: getService,annotate: annotate,has: has }
同样, instanceCache:{
$injector:{
invoke: invoke,annotate: annotate,has: has
}
}
instanceInjector = {
invoke: invoke,has: has
}
两个重要方法: function loadModules(modulesToLoad){
//刚才说了,modulesToLoad长这样:['ng',['$provider','myApp']
forEach(modulesToLoad,function(module) {
if (isString(module)) {
// module为字符串时进入此判断。
moduleFn = angularModule(module);
//迭代,把所有依赖模块的runBlocks都取出
runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
//前面已经提到,每个module下有个_invokeQueue存了一堆controller,service之类东西,现在就可以遍历拿出来运行啦
for(invokeQueue = moduleFn._invokeQueue,i = 0,ii = invokeQueue.length; i < ii; i++) {
//还记得我刚才说了具体怎么调用的把:第一个参数调用名为第二个参数的方法,传入第三个参数
var invokeArgs = invokeQueue[i],provider = providerInjector.get(invokeArgs[0]);
provider[invokeArgs[1]].apply(provider,invokeArgs[2]);
}
}else if(isFunction(module)){
//这个我还没找到…………
}else if(isArray(module)){
//这里就是第二参数的情形了,用invoke方法执行后将结果存到runBlocks
runBlocks.push(providerInjector.invoke(module));
}
}
return runBlocks;
}
重量级函数 function createInternalInjector (){
function getService(serviceName) {};
function invoke(fn,self,locals){};
function instantiate(Type,locals) {};
return {
invoke: invoke,get: getService,has: function(name) {
//
}
};
}
这么重要的函数实际上是一个工厂,最后返回五个方法。下面一一分析:
function annotate(fn) {
var $inject,fnText,argDecl,last;
if (typeof fn == 'function') {
if (!($inject = fn.$inject)) {
$inject = [];
if (fn.length) {
fnText = fn.toString().replace(STRIP_COMMENTS,'');
argDecl = fnText.match(FN_ARGS);
forEach(argDecl[1].split(FN_ARG_SPLIT),function(arg){
arg.replace(FN_ARG,function(all,underscore,name){
$inject.push(name);
});
});
}
fn.$inject = $inject;
}
} else if (isArray(fn)) {
last = fn.length - 1;
assertArgFn(fn[last],'fn');
$inject = fn.slice(0,last);
} else {
assertArgFn(fn,'fn',true);
}
return $inject;
}
传参有两种形式,1.
function invoke(fn,locals){
var args = [],$inject = annotate(fn),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)
);
//省略若干
//switch做了优化处理,罗列了一些常见的参数个数
switch (self ? -1 : args.length) {
case 0: return fn();
//省略若干
}
app.provider('myProvider',function(){
//do something
this.$get = function(){
return obj;
}
});
假如我们想要在angular中用一些设计模式,我们换一种写法: app.provider('myProvider',myProvider);
function myProvider(){
BaseClass.apply(this,arguments);
}
unit.inherits(BaseClass,myProvider);
extend(myProvider,{
$get:function(){
return something
}
});
这样也可以实现我们需要的,而且可扩展。但如果我们没有了解过
万事俱备,只欠东风做了这么多准备,各种 injector.invoke(['$rootScope',animate) {
scope.$apply(function() {
element.data('$injector',injector);
compile(element)(scope);
});
}]
);
通过 再利用 到此,执行流程也就都出来了。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |