STAY HUNGRY , STAY FOOLISH.

求知若饥,虚心若愚。

       浏览:

初识AngularJS

什么是Angular?

从Angular的Github官网上可以看出创造者对该框架的简单定义:HTML enhanced for web apps.即Angular主要是针对Web应用,对HTML标签进行了增强。


使用Angular的好处?

Angular的使用在我看来和JavaEE的有些理念挺相似,什么依赖注入,控制反转,双向绑定等概念。那么使用Angular主要运用在哪些Web应用上呢?它给Web前端带来的好处又有哪些呢?
从Github上看使用Angular可以给开发者的带来的优势有以下几点:

  • 可以生成一个个模板引擎
  • UI表单的双向绑定
  • 实现依赖注入、控制反转
  • 解决异步回调

运用场合:经常CRUD的Web应用
AngularJS的语法及概念在这就不一一细说了,因为里面涉及到的东西太多,去官网看API和网上教程就行。想了解一个框架的本身,不在于可以用它实现什么,而在于它是怎么实现的。

下面的内容主要围绕”怎么实现”及”项目实战”开讲。


怎么实现

Angular核心原理

  • Angular启动过程分析
  • Provider与Injector执行过程
  • 指令的执行过程
  • $scope与双向数据绑定执行过程

Angular启动过程分析

源码解析Angular启动过程分析步骤:
1.自执行加载完整个angular.js暴露全局变量angular

1
2
3
4
5
(function(window,document){
...
var angular = window.angular || (window.angular = {});
...
})(window,document);

2.是否存在angular对象

1
2
3
4
if (window.angular.bootstrap) {
console.log('WARNING: Tried to load angular more than once.');
return;
}

两种启动方式:

1
2
3
4
5
6
7
8
9
10
11
//自动启动:ng-app
<html ng-app='moduleName'>
//code
</html>

//手动启动:
<script>
angular.element(document).ready(function(){
angular.bootstrap(document,['moduleName']);
});
</script>

3.绑定jQuery

1
bindJQuery();

该方法判断用户是否自己导入jQuery,如果没有就导入jQlite:

1
2
3
4
5
6
7
8
9
function bindJQuery(){
var jQuery = window.jQuery;
if (jQuery && jQuery.fn.on) {
jqLite = jQuery;
}else{
jqLite = JQLite;
}
angular.element = jqLite;
}

4.注入angularAPI

1
publishExternalAPI(angular);

该方法给全局变量angular扩展方法及属性,构建模块加载器,注入内置provider注册器和ng指令:

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
function publishExternalAPI(angular) {
extend(angular, {
'bootstrap': bootstrap,
'copy': copy,
'extend': extend,
'merge': merge,
'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},
'getTestability': getTestability,
'$$minErr': minErr,
'$$csp': csp,
'reloadWithDebugInfo': reloadWithDebugInfo
});
angularModule = setupModuleLoader(window);
};

5.初始化

1
2
3
jqLite(document).ready(function() {
angularInit(document, bootstrap);
});

该方法查找ng-app,如果找到执行bootstrap方法,没找到执行手动启动的bootstrap方法

1
2
3
4
5
6
function angularInit(element, bootstrap){
if (appElement) { //ng-app
config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
bootstrap(appElement, module ? [module] : [], config);
}
}

bootstrap方法创建注册器,开始编译

1
2
3
4
5
6
7
8
9
10
11
12
13
function bootstrap(element, modules, config){
...
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);
});
}]
);
...
})

源码解析第5步之Provider与Injector执行过程

看源码的地方搜createInjector:

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
function createInjector(modulesToLoad, strictDi) {
function provider(name, provider_) {
...
}
function factory(name, factoryFn, enforce) {
return provider(name,{$get: ...});
}
function service(name, constructor) {
return factory(name, ['$injector',...]);
}
function value(name, val) {
return factory(name, valueFn(val), false);
}
function constant(name, value) {
...
}
function decorator(serviceName, decorFn) {
...
};
return {
invoke: invoke,
instantiate: instantiate,
get: getService,
annotate: createInjector.$$annotate,
has: function(name) {return ...}
};
}
});

Provider目的是让接口和实现分离。
进行注注入的有:provider、factory、service、constant、value
上面方法核心都是provider实现的,只是参数不同,从左到右灵活性越来越差。

接受注入的有:controller、config、module、run、filter等

注入的方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
var app=angular.module('demo',[]);
// 推断型注入
app.controller('ctrl',function($scope){
//code
});
//声明式注入
var ctrl=function(){//code};
ctrl.$inject=['$scope'];
app.controller('ctrl',ctrl);
//内联式注入
app.controller('ctrl',['$scope',function($scope){
//code
}]);

内置Inject注册器有:

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
$provide.provider({
$anchorScroll: $AnchorScrollProvider,
$animate: $AnimateProvider,
$$animateQueue: $$CoreAnimateQueueProvider,
$$AnimateRunner: $$CoreAnimateRunnerProvider,
$browser: $BrowserProvider,
$cacheFactory: $CacheFactoryProvider,
$controller: $ControllerProvider,
$document: $DocumentProvider,
$exceptionHandler: $ExceptionHandlerProvider,
$filter: $FilterProvider,
$interpolate: $InterpolateProvider,
$interval: $IntervalProvider,
$http: $HttpProvider,
$httpParamSerializer: $HttpParamSerializerProvider,
$httpParamSerializerJQLike:$HttpParamSerializerJQLikeProvider,
$httpBackend: $HttpBackendProvider,
$location: $LocationProvider,
$log: $LogProvider,
$parse: $ParseProvider,
$rootScope: $RootScopeProvider,
$q: $QProvider,
$$q: $$QProvider,
$sce: $SceProvider,
$sceDelegate: $SceDelegateProvider,
$sniffer: $SnifferProvider,
$templateCache: $TemplateCacheProvider,
$templateRequest: $TemplateRequestProvider,
$$testability: $$TestabilityProvider,
$timeout: $TimeoutProvider,
$window: $WindowProvider,
$$rAF: $$RAFProvider,
$$jqLite: $$jqLiteProvider,
$$HashMap: $$HashMapProvider,
$$cookieReader: $$CookieReaderProvider
});

源码解析第5步之指令的执行过程

看源码的地方搜compile:

1
2
3
4
5
6
7
function compile($compileNodes,transcludeFn,maxPriority,ignoreDirective,previousCompileContext){
compile.$$addScopeClass($compileNodes);
var compositeLinkFn=compileNodes(...);
return function publicLinkFn(scope,cloneConnectFn,options){
...
});
};

指令的compile与link:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var app=angular.module('demo',[]);
app.directive('Hello',function(){
return{
restrict:'EA',
template:'<div>Hello</div>',
replace:true,
//一般不使用comile,使用link
link:function(scope,element,attrs,controller){
//el的获取设置attrs、scope或注册事件
},
compile:function(element,attrs,transclude){
//code
return function(scope,element,attrs,controller){
//...
}
}
};
});

compile指令作用是对指令的模板进行转换;
link指令作用是在模型和视图之间建立关联,元素上的注册监听事件等;

内置指令有:

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
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,
pattern: patternDirective,
ngPattern: patternDirective,
required: requiredDirective,
ngRequired: requiredDirective,
minlength: minlengthDirective,
ngMinlength: minlengthDirective,
maxlength: maxlengthDirective,
ngMaxlength: maxlengthDirective,
ngValue: ngValueDirective,
ngModelOptions: ngModelOptionsDirective
})

$scope与双向数据绑定执行过程

看源码的地方搜scope:

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
function Scope() {
this.$id = nextUid();
this.$$phase = ... = null;
this.$root = this;
this.$$destroyed = false;
this.$$listeners = {};
this.$$listenerCount = {};
this.$$watchersCount = 0;
this.$$isolateBindings = null;
}
Scope.prototype = {
constructor: Scope,
$new: function(isolate, parent){...},
$watch: function(watchExp, listener,...) {},
$watchGroup: function(watchExp, listener) {},
$watchCollection: function(obj, listener) {},
$digest: function() {},
$destroy: function() {},
$eval: function(expr, locals) {},
$evalAsync: function(expr, locals) {},
$$postDigest: function(fn) {},
$apply: function(expr) {},
$applyAsync: function(expr) {},
$on: function(name, listener) {},
$emit: function(name, args) {},
$broadcast: function(name, args) {}
};
var $rootScope = new Scope();
...
compile.$$addScopeClass($compileNodes);
...

双向数据绑定:一维结构(表单)、二维结构(表格)、Tree型结构(建议不用双向绑定)


项目实战

在使用Angular开始应用的时候,需要明确一些步骤:

  1. 界面原型设计
  2. 搭建目录结构
  3. 选择UI框架编写UI
  4. 编写Controller
  5. 编写Service
  6. 编写Filter
  7. 测试

有空在细说每个步骤的内容,So Sorry!