AngularJS 简介
AngularJS 是一款由 Google 公司开发维护的前端 MVC 框架,与我们常用的 jQuery 有很大不同。jQuery 更准确来说只是一个类库(类库指的是一系列函数的集合),以 DOM 做为驱动(核心),而 AngularJS 则一个框架(诸多类库的集合),以数据和逻辑做为驱动(核心)。
AngularJS 有着诸多特性,最为核心的是:模块化、双向数据绑定、语义化标签、依赖注入等。
模块化
定义应用
通过为任一 HTML 标签添加 ng-app 属性,可以指定一个应用,表示此标签所包裹的内容都属于应用(App)的一部分。
一个页面可以有多个 ng-app ,但是不能嵌套, ng-app 可以不赋值,但是要关联相应模块时则必须赋值。
1 | <body ng-app="App"> |
定义模块
AngularJS 提供了一个全局对象 angular ,在此全局对象下存在若干的方法,其中 angular.module() 方法用来定义一个模块。
1 | // 通过 module 方法定义模块 |
注意:应用(App)其本质也是一个模块。
定义控制器
控制器(Controller)作为连接模型(Model)和视图(View)的桥梁存在,所以当我们定义好了控制器以后也就定义好了模型和视图。
1 | // 模块返回值也是一个对象,通过此对象方法(controller)可以创建控制器 |
指令
所谓指令就是 AngularJS 自定义的 HTML 属性或标签,这些指令都是以 ng- 做为前缀的,例如 ng-app 、 ng-controller 、 ng-repeat 等。
ng-repeat 处理重复值,使用
track by $index
1 | $scope.items = [ |
内置指令
ng-app指定应用根元素,至少有一个元素指定了此属性。ng-controller指定控制器ng-show控制元素是否显示,true显示、false不显示ng-hide控制元素是否隐藏,true隐藏、false不隐藏ng-if控制元素是否“存在”,true存在、false不存在ng-src增强图片路径ng-href增强地址ng-class利用布尔值控制类名,以便通过数据驱动视图。允许传递一个对象 ng-class=”{red: true, blue: true}”ng-include引入模板,需要以服务器的访问方式,ng-clude=”‘header.html’”ng-disabled表单禁用,ng-disabled=”true”ng-readonly表单只读ng-checked单/复选框表单选中ng-selected下拉框表单选中
自定义指令
AngularJS 允许自定义指令,通过 angular 全局对象下的 directive 方法实现。
1 |
|
数据绑定
所谓数据绑定指的就是将模型(Model)中的数据与相应的视图(View)进行关联
单向绑定
传统的单向数据绑定,指的是将模型(Model)数据,按着写好的视图(View)模板生成 HTML 标签,然后追加到 DOM 中显示,如 artTemplate 模板引擎的工作方式。通常由模型数据决定视图显示效果。
双向绑定
可以实现模型(Model)数据和视图(View)模板的双向传递
ng-cloak防止双重大括号解析闪烁ng-bind-template可以绑定多个数据ng-init初始化模型数据ng-dblclickng-blurng-keyupng-switch on过滤循环数据ng-switch-when
1 |
|
作用域
每个控制器(Controller)对应一个模型(Model)也就是 $scope 对象,不同层级控制器(Controller)下的 $scope 便产生了作用域。
根作用域
一个 AngularJS 的应用(App)在启动时会自动创建一个根作用域 $rootScope ,这个根作用域在整个应用范围( ng-app 所在标签以内)都是可以被访问到的。
子作用域
通过 ng-controller 指令可以创建一个子作用域,新建的作用域可以访问其父作用域的数据。
过滤器
在双重大括号内使用 “|” 来调用过滤器,使用 “:” 传递参数。
内置过滤器
currency将数值格式化为货币格式date日期格式化,年(y)、月(M)、日(d)、星期(EEEE/EEE)、时(H/h)、分(m)、秒(s)、毫秒(.sss),也可以组合到一起使用。filter在给定数组中选择满足条件的一个子集,并返回一个新数组,其条件可以是一个字符串、对象、函数json将 Javascrip 对象转成 JSON 字符串。limitTo取出字符串或数组的前(正数)几位或后(负数)几位lowercase将文本转换成小写格式uppercase将文本转换成大写格式number数字格式化,可控制小位位数orderBy对数组进行排序,第1个参数表示按照什么属性排序,第2个参数控制排序方向
1 |
|
自定义过滤器
根据业务需要自定义过滤器,通过模块对象实例提供的 filter 方法自定义过滤器。
1 |
|
依赖注入
AngularJS 采用模块化的方式组织代码,将一些通用逻辑封装成一个对象或函数,实现最大程度的复用,这导致了使用者和被使用者之间存在依赖关系。
推断式注入
没有明确声明依赖,AngularJS会将函数参数名称当成是依赖的名称。
1 | // AngularJS 内置一些具有特殊功能的“模块” |
注意: 这种方式会带来一个问题,当代码经过压缩后函数的参数被压缩,这样便会造成依赖无法找到。
行内注入
以数组形式明确声明依赖,数组元素都是包含依赖名称的字符串,数组最后一个元素是依赖注入的目标函数。
推荐使用这种方式声明依赖
1 | // 行内注入方式 |
服务
服务是一个对象或函数,对外提供特定的功能。
内置服务
$location是对原生 Javascript 中location对象属性和方法的封装。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21App.controller('DemoController', ['$scope', '$location', function($scope, $location) {
// 通过依赖注入的方式将 $scope 和 $location 两个服务注入进来了
$scope.title = '$location服务';
console.log($location);
$scope.absUrl = $location.absUrl();
$scope.host = $location.host();
$scope.port = $location.port();
$scope.protocol = $location.protocol();
// 与原生 pathname 不同,只代表 # 之后的字符
$scope.path = $location.path();
// 与原生 search 不同,只代表 # 之后的查询字符串,会自动转化为 json 对象
$scope.search = $location.search();
// 哈希,与原生 hash 不同,只代表 # 后面的 # 之后的字符
$scope.hash = $location.hash();
}]);
// 原生 location 对象
for(var key in location) {
console.log(key + '~~~' + location[key]);
}$timeout&$interval1
2
3
4
5
6
7
8
9
10
11
12App.controller('DemoController', ['$scope', '$timeout', '$interval',function ($scope, $timeout, $interval) {
$timeout(function () {
$scope.time = '123';
}, 3000);
var stopTime = $interval(function () {
$scope.now = new Date();
}, 1000);
$scope.stop = function () {
// 停止定时器
$interval.cancel(stopTime);
}
}]);$filter格式化数据。1
2
3
4
5
6
7
8
9
10
11
12App.controller('DemoController', ['$scope', '$filter', function ($scope, $filter) {
// $filter这个服务可以“生产”很多函数,这些可以实现不同的数据格式化任务,通过传递不同的参数实现的
// $filter 负责“造”新过滤器,根据传递的参数不同,过滤器也不一样
var date = $filter('date');
$scope.now = date(new Date(), 'y/M/d');
var currency = $filter('currency');
$scope.price = currency(12.345, '¥');
var limitTo = $filter('limitTo');
$scope.items = limitTo(['html', 'js', 'css'], 2);
var uppercase = $filter('uppercase');
$scope.str = uppercase('hello servcie');
}]);$log打印调试信息。- $log.log(‘日志`)
- $log.info(‘信息’)
- $log.warn(‘警告’)
- $log.error(‘错误’)
- $log.debug(‘调试’)
$http用于向服务端发起异步请求。- get 传参采用
params: {sex: '男'}方式 - post 传参=采用
data: 'name=xiaomi&age=10'方式,而且 post 也可以传递params参数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
28App.controller('DemoController', ['$scope', '$http', '$log',function($scope, $http, $log) {
// AngularJS也对XMLHttpRequest对象进行封装,封装成了一个服务叫 $http,是一个函数
// var xhr = new XMLHttpRequest;
// xhr.open('post', './example.php');
// xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
// xhr.send('name=xiaomi&age=10');
// xhr.onreadystatechange = function () {
// if(xhr.readyState == 4 && xhr.status == 200) {
// xhr.responseText;
// }
// }
$http({
url: 'example.php',
method: 'post',
headers: {
'Content-type': 'application/x-www-form-urlencoded'
},
// restful 接口
// data: {name: 'xiaomi', age: 10}
data: 'name=xiaomi&age=10'
}).success(function (info) {
// console.log(info);
$log.info(info);
$scope.stars = info;
});
}])
- get 传参采用
自定义服务
所谓服务是将一些通用性的功能逻辑进行封装方便使用,自定义服务就是要返回一个对象或函数以供使用。
factory方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20App.factory('showTime', ['$filter', function ($filter) {
// 返回一个函数形式
// return function () {
// var now = new Date();
// var date = $filter('date');
// return date(now, 'y-M-d H:m:s');
// }
// 返回一个对象形式
var now = new Date();
var date = $filter('date');
return {
now: date(now, 'y-M-d H:m:s')
}
}]);
App.controller('DemoController', ['$scope', 'showTime', function($scope, showTime) {
// $scope.now = showTime();
$scope.now = showTime.now;
}])service方法 –> 不需要return1
2
3
4
5
6
7
8
9
10
11
12var App = angular.module('App', []);
// 自定义服务显示日期
App.service('showTime', ['$filter', function($filter) {
var now = new Date();
var date = $filter('date');
this.now = date(now, 'y-M-d H:mm:ss');
}]);
App.controller('DemoController', ['$scope', 'showTime', function($scope, showTime) {
$scope.now = showTime.now;
}])value方法定义常量1
2
3
4
5
6
7
8
9
10
11
12
13// 自定义常量服务
App.value('author', 'xiaomi');
App.value('version', '1.0');
// 本质上一个服务
// 从表现形式上是一个常量
// 常量就是不变的值与变量名相对应
// 声明依赖调用服务
App.controller('DemoController', ['$scope', 'author', 'version', function($scope, author, version) {
$scope.author = author;
$scope.ver = version;
}]);
模块加载
AngularJS 模块可以在被加载和执行之前对其自身进行配置。
AngularJS 执行流程
- 启动阶段(startup)
- 开始
- 浏览器解析 DOM 树
- 遇到 angular.js 停止解析,开始执行脚本
- Angular 监听到 DOMContentLoaded 事件,表示 DOM 树构建完毕
- 启动 Angular 应用
- 初始化阶段(initial)
- 查找模块依赖
- 寻找 ng-app 指令
- 初始化必要组件($injector/$compile/$rootScope),准备默认的注入器(依赖注入)、编译器(ng指令与双重大括号解析)、根作用域(准备存储初始化数据)
- 配置与运行,在 angular 解析 DOM 数之前提前配置或者运行注入器与编译器,可以使用默认配置,省略此步骤
- angular 开始解析 DOM 树
- 编译、链接(compile/link)
- $compile 遍历 DOM 树,搜集解析指令
- 执行每个指令的 compile 函数
- 处理 DOM 转换,编译模板
- 调用链接函数,生成实时视图
- 运行阶段(running)
- 等待事件触发,执行 $digest 循环
- 检测到变化,调用 $watch 函数
- 再次执行 $digest 循环,直到没有变化
- 结束
配置块
通过 config 方法实现对模块的配置, AngularJS 中的服务大部分都对应一个“provider”,用来执行与对应服务相同的功能或对其进行配置。
1 | // 配置$log服务(禁用debug),可以同时配置两个服务 |
运行块
不需要将服务作为依赖注入到 ng-controller 模块中,可以直接运行相应的服务模块, AngularJS 提供了 run 方法来实现。
1 | var App = angular.module('App', []); |
而且, run 方法还是最先执行的,利用这个特点我们可以将一些需要优先执行的功能通过 run 方法来运行,比如验证用户是否登录,未登录则不允许进行任何其它操作。
路由
一个应用是由若干个视图组合而成的,根据不同的业务逻辑展示给用户不同的视图,路由是实现这一功能的关键。
SPA
SPA(Single Page Application)指的是通单一页面展示所有功能,通过 Ajax 动态获取数据然后进行实时渲染,结合 CSS3 动画模仿原生 App 交互,然后再进行打包(使用工具把 Web 应用包一个壳,这个壳本质上是浏览器)变成一个“原生”应用。
SPA 在 PC 端也有广泛的应用,通常情况下使用 Ajax 异步请求数据,然后实现内容局部刷新。
前端路由
在后端开发中通过 URL 地址可以实现页面(视图)的切换,但是 AngularJS 是一个纯前端 MVC 框架,在开发单页面应用时,所有功能都在同一页面完成,所以无需切换 URL 地址(即不允许产生跳转),但 Web 应用中又经常通过链接(a标签)来更新页面(视图),当点击链接时还要阻止其向服务器发起请求,通过锚点(页内跳转)可以实现这一点。
实现单页面应用需要具备:
- 只有一页面
- 链接使用锚点
单页面应用原理 是通过 hashchange 事件监听到锚点的变化,从而实现为不同的锚点准备不同的视图。
angular-route.js 对这一实现原理进行了封装,将锚点的变化封装成路由(Route),这是与后端路由的根本区别。
路由参数
ngRoute提供两个方法匹配路由,分别是when和otherwise,when方法需要两个参数,otherwise方法作为when方法的补充只需要一个参数,其中when方法可以被多次调用。- 第1个参数是一个字符串,代表当前 URL 中的 hash 值。
- 第2个参数是一个对象,配置当前路由的参数,如视图、控制器等。
- template 字符串形式的视图模板
- templateUrl 引入外部视图模板
- controller 视图模板所属的控制器
- redirectTo跳转到其它路由(一般用于 otherwise 参数对象中)
- 获取参数,在控制中注入
$routeParams可以获取传递的参数
1 |
|
其它
AngularJS 本身实现了简版的 jQuery Lite ,如果没有引入 jQuery ,那么通过 angular.element(原生DOM) 不能选择元素,但是可以将一个 DOM 元素转成 jQuery 对象。
如果引提前引入了 jQuery ,则 angular.element 完全等于 jQuery 或者 $ 。