AngularJS框架研究(一)
1
2
3
4
5
6
7
8
9
10
11
|
//try to bind to jquery now so that one can write angular.element().read()
//but we will rebind on bootstrap again.
bindJQuery();
publishExternalAPI(angular);
<span style=
"color: #ff0000;"
>jqLite(document).ready(
function
() {
angularInit(document, bootstrap);
});</span>
})(window, document);
|
红色部分标出的正是AngularJS的入口。AngularJS内置了jQuery的轻量版本jqLite,具体代码见src/jqLite.js。bindJquery函数会尝试去绑定jQuery库,如果没有找到,就用内置的jqLite。DOM加载完毕后,执行angularInit函数,作准备工作。
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
|
function
<strong>angularInit</strong>(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*([\w\d_]+);?)?\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) {
<span style=
"color: #ff0000;"
>bootstrap</span>(appElement, module ? [module] : []);
}
}
|
angularInit函数主要用来寻找主程序入口。如果在DOM中找到了ng-app标记,则调用bootstrap开始初始化框架。如果没有定义app标记,则需要手动调用angular.bootstrap来初始化。app标记一般在html节点,也可以放置在任意的节点上,app节点所在的DOM树都会被AngularJS框架遍历解析。
ng-app属性如果有值,即自定义module,也会被解析出来,前提是我们必须先创建module,用来管理全局的injector行为和对象。如果没有值,则会创建默认的module。
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
|
function
<strong>bootstrap</strong>(element, modules) {
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 || [];
modules.unshift([
'$provide'
,
function
($provide) {
$provide.value(
'$rootElement'
, element);
}]);
modules.unshift(
'ng'
);
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);
});
animate.enabled(
true
);
}]
);
return
injector;
};
var
NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
if
(window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
return
doBootstrap();
}
window.name = window.name.replace(NG_DEFER_BOOTSTRAP,
''
);
angular.resumeBootstrap =
function
(extraModules) {
forEach(extraModules,
function
(module) {
modules.push(module);
});
doBootstrap();
};
}
|
按照官方文档描述,bootstrap步骤分为三步:首先加载module,然后创建全局injector对象,最后执行compile动作,对应的正是上面的代码。
AngularJS会为每个应用程序创建唯一的injector对象,它可以看作是一个对象池,依靠键值来存取实例,比如存放数据的model,和后端交互的service等,而实例的创建则由module来决定。
在AngularJS框架中,应用程序被看作是由多个module组成的一个结合体,而一个module,往往是相似功能块的组合。在一个大型应用程序中,我们习惯将程序切分为多个模块并行开发,这也是AngularJS的推荐做法。在module中,可以定义和View层打交道的Controller,和后台交互的Service,也可以自定义依赖注入行为,解析特殊的DOM数据。
从上面的代码可以看到,初始化时会加载内置的模块,比如“ng”。在ng模块中定义了AngularJS的核心功能,包括解析DOM树中的以“ng”为前缀的自定义节点, 比如ng-model,ng-class,ng-repeat等等。
compile,编译,顾名思义,就是将DOM中的ng标记和其他自定义标记解析为真正的View, Model和Controller等。
--------------------
到这里为止,对AngularJS的运行机制还处于一知半解,不得不吐槽下,想要弄懂AngularJS的运行机制实在不是件容易的事。
最后再补充一点:有一款名为"Batarang"的Chrome扩展插件一定不能错过,AngularJS开发调试必备。用过后,才知道官网上一些截图是怎么来的了~