什么是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
(function(window,document){
...
var angular = window.angular || (window.angular = {});
...
})(window,document);
2.是否存在angular对象
if (window.angular.bootstrap) {
console.log('WARNING: Tried to load angular more than once.');
return;
}
两种启动方式:
//自动启动:ng-app
<html ng-app='moduleName'>
//code
</html>
//手动启动:
<script>
angular.element(document).ready(function(){
angular.bootstrap(document,['moduleName']);
});
</script>
3.绑定jQuery
bindJQuery();
该方法判断用户是否自己导入jQuery,如果没有就导入jQlite:
function bindJQuery(){
var jQuery = window.jQuery;
if (jQuery && jQuery.fn.on) {
jqLite = jQuery;
}else{
jqLite = JQLite;
}
angular.element = jqLite;
}
4.注入angularAPI
publishExternalAPI(angular);
该方法给全局变量angular扩展方法及属性,构建模块加载器,注入内置provider注册器和ng指令:
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.初始化
jqLite(document).ready(function() {
angularInit(document, bootstrap);
});
该方法查找ng-app,如果找到执行bootstrap方法,没找到执行手动启动的bootstrap方法
function angularInit(element, bootstrap){
if (appElement) { //ng-app
config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
bootstrap(appElement, module ? [module] : [], config);
}
}
bootstrap方法创建注册器,开始编译
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:
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等
注入的方式:
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注册器有:
$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:
function compile($compileNodes,transcludeFn,maxPriority,ignoreDirective,previousCompileContext){
compile.$$addScopeClass($compileNodes);
var compositeLinkFn=compileNodes(...);
return function publicLinkFn(scope,cloneConnectFn,options){
...
});
};
指令的compile与link:
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指令作用是在模型和视图之间建立关联,元素上的注册监听事件等;
内置指令有:
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:
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开始应用的时候,需要明确一些步骤:
- 界面原型设计
- 搭建目录结构
- 选择UI框架编写UI
- 编写Controller
- 编写Service
- 编写Filter
- 测试
有空在细说每个步骤的内容,So Sorry!