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()函数中。