angularjs学习总结(快速预览版)

对html标签的增强 -> 指令

指令的本质是什么 声明的方式调用相应的脚本,实现一些操作,声明的所在的dom就是脚本的执行上下文?

自定义标签 -- 标签指令
自定义属性 -- 属性指令
特定格式注释 -- 注释指令

概念
模板 视图 模型 模块 指令 依赖注入 路由

> MVVM
其他MVC框架 backbone emberjs
Angular不依赖其他任何框架
AngularJs重新定义了前端应用的开发方式,AngularJs特别适合开发单页面应用(SPA)

> AngularJs的特性
1. 双向数据绑定

2. dom模板
简单的来说,模板是包含angular指令和表达式的html文件
html模板---浏览器解析--->dom模板---Angular编译器编译--->动态dom(双向数据绑定)
因为是dom模板,所以angular可以自定义具有特定行为的标签或属性(指令)


3. mvc
model:
数据,一般是简单的js对象

viewModel:
是一个提供特别数据和方法从而维护指定view的对象。viewmodel是$scope的对象,只存在于AnguarJS的应用中。$scope只是一个简单的js对象,这个对象使用简单的API来侦测和广播状态变化。

controller:
controller负责设置初始状态和参数化$scope方法用以控制行为。需要指出的controller并不保存状态也不和远程服务互动。

view:
view是AngularJS解析渲染和绑定后生成的HTML

4. 服务和依赖注入
服务就是对外提供某种功能
内置DI(依赖注入)系统,自动查找和注入依赖,有利于开发,理解和测试

//例子1:创建自定义服务,并注入到控制器中
html template:
<div ng-controller="myctrl">
<p ng-click="hi('long time no see, how are you today?');">点击我打个招呼吧</p>
</div>
js:
angular.module('myapp',[]).factory('sayhi',[$window, function(win){
return function(msg){
win.alert('hi, sindy!' + msg );
}
}]);

function myctrl(scope,sayhiService){
scope.hi = function(msg){
sayhiService.hi(msg);
};
}

myctrl.$inject = ['$scope', 'sayhi'];


//例子2:视图-控制器-模型的交互
<html ng-app="myapp">
...
<div ng-controller="demoCtrl">
<input type="text" ng-model="user.name" disabled="disabled" />
<a href="#" ng-click="getAjaxUser();">Ajax获取用户名</a>
</div>
...
js:
mymodule = angular.module('myapp',[]);
mymodule.controller('demoCtrl',function($http, $scope){
$scope. getAjaxUser = function(){
// $http.get('user.php').success(function(data){ //ok too
$http.get('user.json').success(function(data){
$scope.user = data;
});
};
});

5. scope, module, controller
scope:
view和controller都可以访问$scope中保存的数据和方法,所以$scope是view和controller通信的桥梁。
在mvc结构里,$scope扮演者model的角色,负责提供数据和方法,这些数据和方法是在某个Dom元素范围内可用的。

每个angular应用都会有一个$rootScope,即可顶级的scope,对应的包含ng-app指令属性的dom元素

模板表达式 {{}}

module:
<html ng-app="myapp">
var mymodule = angular.module('myapp',[]);

controller:
<div ng-controller="myCtrl">{{person.name}}</div>
ng-controller 指令属性给所在dom元素创建了一个$scope对象,外层元素也有$scope对象,所有的$scope形成作用域链,
子$scope对象是以原型继承的方式继承自父$scope的,任何属性和方法的访问都是从当前$scope查找--没找到,则在父$scope查找,层层向上遍历的(和js作用域链的查找机制一样)。

注意:有些指令属性(如:ng-repeat)可以选择性地创建一个独立的scope,让这个scope不继承它的父scope们。

6. ajax 和 jsonp
通过注入$http服务来使用ajax的相关方法,如:$http.get(url).success(fn).error(fn);

7. expression 表达式 ~~模板表达式?
表达式可以作为指令的值 如:ng-model="person.name" ng-click="showme()"
标记中使用表达式: {{2+3}} {{2+3|currency}}
ng的表达式不能使用循环,判断语句,不推荐使用复杂的表达式,表达式中尽量不包含逻辑

--所有表达式都是在当前scope这个上下文执行,因此可以访问$scope中的变量
--表达式的执行出现类型错误或引用错误,错误不会被抛出
--表达式里不允许流程控制语句(if/else)
--表达式可接受1个或多个串联的过滤器

8. filter
filter的作用对数据进行格式化
{{expresion | filter1 | filter2... }} 前1个filter的输出作为下一个filter的输入

{{expression | filter:arg1:arg2 }} filter接受参数

<div ng-repeat=" img in images | filter"></div>
在js中使用filter,以依赖注入的方式使用。
mymodule.controller('myCtrl', function($scope, currencyFilter){
$scope.num = currencyFilter(12345);
});

注入多个过滤器
mymodule.controller('myctrl',function($scope, $filter){
//...
})

ng的内置过滤器:
number, currency, date
filter, limitTo, lowercase, uppercse, number
json, orderBy

9. 指令
模板中可以使用的东西包括4种:
--指令(directive):ng提供的或自定义的标签和属性,用来增强html的表现力(~~个人理解是以声明的方式绑定某种行为)
--标记(markup): {{}} 可将数据单向绑定到视图
--过滤器(filter): 用来格式化数据的输出
--表单控制:用来增强表单验证功能

ng中指令的使用量是最大的,ng内置了很多指令用来控制模板,如ng-repeat,ng-class,也有很多指令来帮你完成业务逻辑,如ng-controller,ng-model。
指令的几种使用方式如下:

--作为标签:<my-dir></my-dir> ~~~注意指令名在js中是 myDir 则在html中是my-dir
--作为属性:<span my-dir="exp"></span>
--作为注释:<!-- directive: my-dir exp -->
--作为类名:<span class="my-dir: exp;"></span>

其实常用的就是作为标签和属性。

9.1 样式相关的指令
ng-class:
ng-class用来给元素绑定类名,其表达式的返回值可以是以下三种:

--类名字符串,可以用空格分割多个类名,如’redtext boldtext’;
--类名数组,数组中的每一项都会层叠起来生效;
--一个名值对应的map,其键值为类名,值为boolean类型,当值为true时,该类会被加在元素上。

ng-style:

ng-show , ng-hide:

9.2 表单控制相关的指令
ng-checked 控制radio和checkbox的选中状态
ng-selected 控制select的选项选中状态
ng-multiple 控制select多选
ng-disabled
ng-readonly
值都为boolean类型,为true时生效;从数据到模板的单向绑定

9.2 事件绑定相关的指令
事件是js中比较重要的一部分内容。
ng-click
ng-dblclick
ng-change

ng-mousedown
ng-mouseup
ng-mouseenter
ng-mouseover
ng-mousemove
ng-mouseleave

ng-submit


10. 特殊的ng-src ng-href
angularjs是在DOMContentLoaded之后才开始解析dom文档的,所以在DOMConetentLoaded之前 src={{url}} href="{{url}}" <p>{{user.name}}</p> 都会明文显示出来,导致链接无效,模板表达式没被内容替换,避免这种情况的方法就是用 ng-src, ng-href, ng-bind

<img ng-src="url" />
<a ng-href="url" />

11. 自定义指令 mymodule.directrive('userInfo', function(){..})

12. 服务 service
服务是一个单例对象或函数,对外提供某种功能,服务是模块内可用的,不污染全局环境
系统内置服务以$开头


13. 路由
angular的路由机制是靠ngRoute模块提供的,通过hash和history两种方式实现路由,特性检测后决定采用哪种方式路由
ngRoute模块包括以下内容:
--$routeProvider服务用来定义一个路由表,即地址栏与视图模板的映射
--$routeParams服务保存了地址栏的参数 如:{id:1,name:'tom'}
--$route服务完成路由匹配,并提供路由相关的服务和事件
--ngView指令用来在主视图中指定加载子视图的区域

以上+$location服务就可以完成一个单页面应用了(SPA)


--引入js文件 angular-route.js
--注入到根作用域对应的控制器中 var mymodule = angular.module('myapp',['ngRoute']); //node的模块定义 加载其他依赖模块

--定义路由表 $routeProvider
when(path, route) otherwise(params)

核心方法 when(path, route)
* (string)path 将与 $location.path进行匹配 如需匹配参数冒号+名称 ,如:"/show/:name" 将匹配地址栏的 /show/tom {name:'tome'}被保存到$routeParams中, *进行模糊匹配 如: /show*/:name 将匹配地址栏的 /showinfo/tom

* (object)route 用于指定path获得匹配后所需的一些配置项 包括
.. (fn|string)controller 在当前模板上执行的controller函数, 生成新的scope
.. (string)controllerAs 设置controller的别名
.. (string|fn)template 视图所用的模板 这部分内容将被ngView引用
.. (string|fn)templateUrl 当视图模板为单独的html文件或是使用了<script type="text/ng-template">定义模板时使用;
.. resolve 指定当前controller所以来的模块
.. redirectTo 重定向的地址

14. angular动画
通过css3或js实现,js的实现底层是通过其他js库来实现的

ng动画效果包括:
--enter 元素添加到dom中时执行动画
--leave 元素从dom中删除时执行动画
--move 移动元素时执行动画
--beforeAddClass
--addClass
--beforeRemoveClass
--removeClass

类似加入路由功能
先引入angular-animate.js
注入动画模块 var mymodule = angular.module('myapp',['ngAnimate']);
引入实现js动画效果的js库 如jq

mymodule.animation('.border-animation', function(){
return {
beforeAddClass: function(element, className, done){
//...
},
removeClass: function(element, className, done){
//...
}
}
});

-----------------------------------
Angularjs进阶
$watch, $apply, $digest, dirty-checking

> $watch
<input name="txtUser" type="text" ng-model="user" />
<input name="txtPass" type="text" ng-model="pass" />
绑定数据模型$scope.user到dom txtUser, $scope.pass绑定到dom txtPass
每绑定一些东西到dom中时,就会在$watch队里中增加一条$watch, $watch监视被绑定的2个东西的变化

每一个绑定到了DOM上的数据都会生成一个$watch,当我们的模版加载完毕时,也就是在linking阶段(Angular分为compile阶段和linking阶段),Angular解释器会寻找每个directive,然后生成每个需要的$watch。

> $digest
浏览器收到可以被angular context处理的事件, $digest就会被触发, evalAsync队列 $watch队列
遍历每条$watch 进行dirty-checking.

如果当事件触发时,你调用$apply,它会进入angular context,如果没有调用就不会进入

$apply是我们的$scope(或者是direcvie里的link函数中的scope)的一个函数,调用它会强制一次$digest循环(除非当前正在执行循环,这种情况下会抛出一个异常,这是我们不需要在那里执行$apply的标志)。

执行$apply:
element.bind('click', function() {
scope.foo++;
scope.bar++;
scope.$apply();
});

更好的使用$apply的方法:
element.bind('click', function() {
scope.$apply(function() {
scope.foo++;
scope.bar++;
});
})

> 自定义$watch
~~~$watch的作用就是监视一个数据模型(如:$scope.name) 当它改变了的话,默认会同步更新跟它($scope.name)绑定的dom, 这里我们可以自定义检测到数据模型更新时 要执行的回调函数。
/*Controller app.js */
app.controller('MainCtrl', function($scope) {
$scope.name = "Angular";
$scope.updated = -1;
$scope.$watch('name', function() {
$scope.updated++;
});
});

/*View index.html*/
<body ng-controller="MainCtrl">
<input ng-model="name" />
Name updated: {{updated}} times.
</body>


dirty-checking的执行是很快的

> 自定义指令详解
angular通过指令的方式实现了HTML的扩展,增强后的HTML不仅长相焕然一新,同时也获得了很多强大的技能。
angular的启动过程:
浏览器解析html=>dom --> angular $compile(查找模板表达式和指令--把指令关联到dom--多个指令按权重排列--执行指令中的complie函数 返回link函数) =>所有指令返回的link函数队列-->执行link函数 连接到scope

这里注意区别一下$compile和compile,前者是ng内部的编译服务,后者是指令中的编译函数,两者发挥作用的范围不同。

 

指令的使用方式和命名:
--作为标签:<my-dir></my-dir>
--作为属性:<span my-dir="exp"></span>
--作为注释:<!-- directive: my-dir exp -->
--作为类名:<span class="my-dir: exp;"></span>

关于自定义指令的命名,你可以随便怎么起名字都行,官方是推荐用[命名空间-指令名称]这样的方式,像ng-controller。
指令命名时用驼峰规则,使用时用-分割各单词。如:定义myDirective,使用时像这样:<my-directive>


自定义指令的配置参数:
myModule.directive('namespaceDirectiveName', function(injectables) {

var directiveDefinitionObject = {

restrict: string,//指令的使用方式,包括标签,属性,类,注释

priority: number,//指令执行的优先级

template: string,//指令使用的模板,用HTML字符串的形式表示

templateUrl: string,//从指定的url地址加载模板

replace: bool,//是否用模板替换当前元素,若为false,则append在当前元素上

transclude: bool,//是否将当前元素的内容转移到模板中

scope: bool or object,//指定指令的作用域

controller: function controllerConstructor($scope, $element, $attrs, $transclude){...},//定义与其他指令进行交互的接口函数

require: string,//指定需要依赖的其他指令

link: function (scope, iElement, iAttrs) {...},//以编程的方式操作DOM,包

括添加监听器等

compile: function(tElement, tAttrs, transclude){

return: {

pre: function preLink(scope, iElement, iAttrs, controller){...},

post: function postLink(scope, iElement, iAttrs, controller){...}

}

}//编程的方式修改DOM模板的副本,可以返回链接函数

};

return directiveDefinitionObject;

});

--指令的表现配置参数:restrict、template、templateUrl、replace、transclude;

--指令的行为配置参数:compile和link;

--指令划分作用域配置参数:scope;

--指令间通信配置参数:controller和require。


> 指令的解析流程:
DOMContentLoaded -> ng查找ng-app ->扫描指令(指令属性 指令元素 插值指令{{}} ) -->(根据指令的配置参数)解析指令为dom(或者说转换为dom)[~~生成html] -->顺序执行各指令的complie函数(指令定义时配置的的complie函数) 访问dom并操作dom 返回link函数[~~绑定监听]--> link函数队列,进入link阶段 把view和scope连接起来 [~~关联数据]

如果指令只进行DOM的修改,不进行数据绑定,那么配置在compile函数中,如果指令要进行数据绑定,那么配置在link函数中。


> 使用scope为指令划分作用域

  顾名思义,scope肯定是跟作用域有关的一个参数,它的作用是描述指令与父作用域的关系,这个父作用域是指什么呢?想象一下我们使用指令的场景,页面结构应该是这个样子:
<div ng-controller="testC">
<say-hello speak="content">美女</say-hello>
</div>  
外层肯定会有一个controller,而在controller的定义中大体是这个样子:
var app = angular.module('MyApp', [], function(){console.log('here')});
app.controller('testC',function($scope){
$scope.content = '今天天气真好!';
}); 

所谓sayHello的父作用域就是这个名叫testC的控制器所管辖的范围,指令与父作用域的关系可以有如下取值:

false 默认值。使用父作用域作为自己的作用域
true 新建一个作用域,该作用域继承父作用域
javascript对象 与父作用域隔离,并指定可以从父作用域访问的变量

scope: {
attributeName1: 'BINDING_STRATEGY',
attributeName2: 'BINDING_STRATEGY',...
}  
键为属性名称,值为绑定策略。等等!啥叫绑定策略?最讨厌冒新名词却不解释的行为!别急,听我慢慢道来。
  先说属性名称吧,你是不是认为这个attributeName1就是父作用域中的某个变量名称?错!其实这个属性名称是指令自己的模板中要使用的一个名称,并不对应父作用域中的变量,稍后的例子中我们来说明。再来看绑定策略,它的取值按照如下的规则:

@ 传递一个字符串作为属性的值 str : ‘@string’

= 使用父作用域中的一个属性,绑定数据到指令的属性中 name : ‘=username’

& 使用父作用域中的一个函数,可以在指令中调用 getName : ‘&getUserName’


> 指令间的通信参数 controller和require

使用指令来定义一个ui组件是个不错的想法,首先使用起来方便,只需要一个标签或者属性就可以了,其次是可复用性高,通过controller可以动态控制ui组件的内容,而且拥有双向绑定的能力。当我们想做的组件稍微复杂一点,就不是一个指令可以搞定的了,就需要指令与指令的协作才可以完成,这就需要进行指令间通信。

想一下我们进行模块化开发的时候的原理,一个模块暴露(exports)对外的接口,另外一个模块引用(require)它,便可以使用它所提供的服务了。ng的指令间协作也是这个原理,这也正是自定义指令时controller参数和require参数的作用。

controller参数用于定义指令对外提供的接口:
controller: function controllerConstructor($scope, $element, $attrs, $transclude)
它是一个构造器函数,将来可以构造出一个实例传给引用它的指令。
先看controller可以使用的参数,作用域、节点、节点的属性、节点内容的迁移,这些都可以通过依赖注入被传进来,所以你可以根据需要只写要用的参数。

表示从父节点上寻找,使用起来像这样:require : ‘^directiveName’,如果不加,$compile服务只会从节点本身寻找。另外还可以使用前缀:?,此前缀将告诉$compile服务,如果所需的controller没找到,不要抛出异常。

> 性能及调优
AnglarJS很棒,但当处理包含复杂数据结构的大型列表时,其运行速度就会非常慢
一个与“ng-repeat ”指令有关,另一个与过滤器有关

AngularJS中的ng-repeat在处理2500个以上的双向数据绑定时速度会变慢。这是由于AngularJS通过“dirty checking”函数来检测变化。每次检测都会花费时间,所以包含复杂数据结构的大型列表将降低你应用的运行速度。

> 7个调优规则:
1.渲染没有数据绑定的列表
数据绑定是性能问题最可能的根源,如果你只想显示一次列表,并不需要更新、改变数据,放弃数据绑定是绝佳的办法。

2.不要使用内联方法计算数据
<li ng-repeat="item in filteredItems()"> //这并不是一个好方法,因为要频繁地评估。
<li ng-repeat="item in items"> //这是要采用的方法

3.使用两个列表(一个用来进行视图显示,一个作为数据源)

4.在其他模板中使用ng-if来代替ng-show
<div ng-if="item.showDetails">
{{item.details}}
</div>


5.不要使用ng-mouseenter、ng-mouseleave等指令
使用内部指令,像ng-mouseenter,AngularJS会使你的页面闪烁。浏览器的帧速率通常低于每秒30帧。使用jQuery创建动画、鼠标悬浮效果可以解决该问题。确保将鼠标事件放入jQuery的.live()函数中。

posted @ 2014-08-20 11:03  stephenykk  阅读(681)  评论(0编辑  收藏  举报