前端开发框架之AngularJS篇

1、 前端,是一种GUI软件

在一个Web系统中,用户通过浏览器上网,输入URL的那一刻起,就意味着通过HTTP协议发送一个远程请求,服务端会将一个URL请求映射到一个具体的URL,然后将关联的资源分别返回给浏览器端,浏览器拿到了这些资源之后,负责解释、执行并呈现(渲染)。

从本质上讲,所有Web应用都是一种运行在网页浏览器中的软件,这些软件的图形用户界面(Graphical User Interface,简称GUI)即为前端。【1】

2、 传统前端文件

HTML(现在常用HTML5),标记语言,负责呈现网页的结构以及内容。打个比方,一栋房子,它有多少层,每一层里面,有几间房间,卧室、厨房、卫生间的分布,玻璃窗户的分布,房子的结构和房子的内容【2】。可以通过静态HTML标记呈现,也可以通过JavaScript编程动态展现。对于JS来说,一个完整的HTML文档就是一棵树,称之为DOM(DocumentObject Model,文档对象模型),对DOM的操作称为DOM操作。

JQuery的DOM操作示例: var p_txt =$("p").attr("title");

CSS(常用CSS3),样式语言,负责网页内容的显示样式。还是上面的例子,一个房子里面某个房间的墙的颜色,窗户的风格等等。

JavaScript,脚本语言。脚本实际上就是为整个HTML Document增加了动态交互的能力。不同的浏览器有着不同的JavaScript实现,所以实现JavaScript跨浏览器兼容成为了后来评论一个JS框架是否实用的一个基本的标准。

3、 JS框架

3.1前端框架类型
图1 常用的一些前端框架

共同特性:

· 选择器(Selector)

· DOM 遍历

· DOM 操作

· 实用(Utility)函数

· 事件处理

· Ajax

3.2 JS框架定义

JavaScript框架或库是一组能轻松生成跨浏览器兼容的JavaScript 代码的工具和函数,每一个库都在众多流行的Web 浏览器的现代版本上进行了可靠的测试.

3.3 为什么要用JS框架?

JavaScript框架或库是一组能轻松生成跨浏览器兼容的 JavaScript 代码;另一个比较有说服力的理由是标准化的跨浏览器 Ajax 请求(Ajax 请求是一个异步 HTTP 请求,通常发送给服务器端脚本,后者返回 XML、JSON、HTML 或普通文本格式的响应)。

4、 AngularJS

AngularJS由 Google 的员工 MiskoHevery 从 2009 年开始着手开发,2012年发布版本1.0。该项目目前已由 Google 正式收购,有一个全职的开发团队继续开发和维护这个库。并且在2014年秋天发布了2.0版本,2016年9月发布了2.0最终版。

4.1 AngularJS的核心概念
图2 AngularJS的七个核心概念【3】
Modules(模块):

在前端开发过程中,通常的做法都是将处理业务逻辑的代码写在一个单独的JS文件中,然后在HTML页面中引入该文件。这样会带来新的问题,我们的控制器全都定义在全局的命名空间中。假设我们有一个公共的JS文件,在登录页面和密码修改页面都引入这个JS,A开发人员和B开发人员碰巧对控制器的命名都是UserController,这样就会导致命名冲突(全局变量污染)【4】。而且我们在新增一个控制器的时候总是要担心是不是已经有了一个同名的控制器,代码的扩展性变得很差。

AngularJS允许我们使用angular.module()方法来声明模块,这个方法接受两个参数,第一个是我们定义的模块名称,第二个是依赖列表,也就是可以被注入到模块中的对象列表。

angular.module(‘myApp’,[]);

对于上述例子,我们可以将不同的控制器封装在不同的模块中,然后在对应的HTML文档中用ng-app指令来指定引用所需要的模块。

图3 模块的引用
图4 模块的定义

使用模块带来的好处:1、保持全局命名空间的清洁;2、易于在不同的应用间复用代码;3、使应用能够以任意顺序加载代码的各个部分。

Components(组件):

组件是Angular2.0中最基本的一个概念。一个组件包含一个视图即我们用来展示信息或者完成用户交互的页面。 技术上来讲, 一个组件就是一个控制模板视图的类, 在开发应用中会写很多组件。

一个应用中总是存在一个根(主)组件,根组件中包含了其他组件。简而言之,每一个Angular 2应用都有一棵对应的组件树。我们应用的组件树看起来是这样的:

图5 组件树【5】
Template(模板):

AngularJS的主旨即快速创建单页面应用,所谓单页面就是说真正的页面只有一个,其中变化的只是模板和数据。在AngularJS中,一个模板就是一个HTML文件,HTML模板将会被浏览器解析到DOM中,DOM然后成为AngularJS编译器的输入,AngularJS将会遍历DOM模板来生成一些指令。

ng-If是个对于模板很重要的指令,它是基本的条件表达,满足条件时则存在,不满足则不存在。通过它可以轻松的让模板基于数据呈现不同结构。

ng-Repeat则是另一重要指令,能循环创建DOM。可以说只要数据中有数组等结构,这一指令就必不可少

ng-Class是样式层面上的主要指令,它的值可以是存放class名的变量,也可以是带有条件的对象

这些基本的指令构成了一套很有效的模板逻辑,我们可以消除掉各种HTML的重复性代码,还能在单个模板中呈现出无数的形式。

以下为一个例子,ng-repeat指令来循环图片数组并且加入img模板

图6 模板示例
Dependency Injection(依赖注入):

一个对象通常有三种方式可以获得对其依赖的控制权,分别是在内部创建依赖;通过全局变量进行引用;通过参数进行传递。我们所说的依赖注入就是通过第三种方式实现的。

DI的代码逻辑:需要什么对象,声明一下即可,由外界的框架创建这些对象,然后根据每个形参的名称来查找依赖对象并且注入进来。若声明形参名称有错误,则无法提供该对象。

图7 依赖注入示例

如果没有明确的声明,AngularJS会假定参数名称就是依赖的名称。因此,它会在内部调用函数对象的ToString()方法,分析并提取出函数参数列表,然后通过$injector(AngularJS中的一种服务)将这些参数注入进对象实例【6】。

Services(服务):

在 AngularJS 中,服务是一个函数或对象,可在AngularJS 应用中使用。AngularJS 内建了一共30 多个服务。

  • 首先是一个单例,即无论这个服务被注入到任何地方,对象始终只有一个实例。
  • 其次服务被定义在一个模块中,所以其使用范围是可以被我们管理的,避免全局变量污染。

AngularJS中几种服务示例:

$http:用于处理 XMLHttpRequest

$location:提供当前URL的信息

$routeProvider:配置路由

$log:日志服务

定义服务有三种方式:使用系统内置的$provide服务;使用Module的factory方法;使用Module的service方法【7】。

//使用factory方法

app.factory('remoteData',function(){

   var data = {name:'n',value:'v'};

   return data;

});
//使用service方法

app.service('remoteData',function(){

   this.name = 'n';

   this.value = 'v';

});
Data Binding(数据绑定):

在传统的web框架中,控制器将多个模型中的数据和模板组合在一起形成视图,并将其提供给用户,如果没有任何自定义的JavaScript组件,这样的视图只会体现出它渲染时模型暴露出的数据。AngularJS创建了一个实时模板来代替视图,任何一个独立视图组件中的值都是动态替换的,这是AngularJS中最重要的功能之一。

图8 数据绑定
图9 数据绑定演示

所谓的双向绑定,无非是从界面的操作能实时反映到数据,数据的变更能实时展现到界面。

<div ng-controller="CounterCtrl">

    <span ng-bind="counter"></span>

    <button ng-click="counter=counter+1">increase</button>

</div>

function CounterCtrl($scope) {

    $scope.counter = 1;

}

The ng-Bind attribute tells Angular to replace the text content of the specified HTML element with the value of a given expression, and to updatethe text content when the value of that expression changes.

AngularJS选择了dirty check。简单来说就是给每个需要绑定的元素加个watcher,缓存下OldValue,然后定时遍历所有的watcher,比较NewValue和OldValue,如果变化了做更新操作。

一、浏览器事件循环和AngularJS拓展

我们的浏览器一直在等待事件,比如用户交互。假如你点击一个按钮或者在输入框里输入东西,事件的回调函数就会在javascript解释器里执行,然后你就可以做任何DOM操作,等回调函数执行完毕时,浏览器就会相应地对DOM做出变化。 Angular拓展了这个事件循环,生成一个angular context的执行环境。

二、watch队列(watch list)

每次你绑定一些东西到你的UI上时你就会往$watch队列里插入一条$watch。$watch就是那个可以检测它监视的model里有变化的东西。当我们的模版加载完毕时,也就是在linking阶段(Angular分为compile阶段和linking阶段),Angular解释器会寻找每个directive,然后生成每个需要的$watch。

三、$digest循环

当浏览器接收到可以被angular context处理的事件时,digest循环就会触发,digest将会遍历我们的watch队列,然后询问它是否有属性和值的变化,直到$watch队列都检查过。这就是所谓的dirty-checking。当所有的$watch都检查完了,如果有至少一个更新过,这个循环就会再次触发,直到所有的$watch都没有变化。这样就能够保证每个model都已经不会再变化,如果循环超过10次的话,它将会抛出一个异常,防止无限循环。 当$digest循环结束时,DOM相应地变化【8】。

Directive(指令)

指令本质上就是AngularJS扩展具有自定义功能的HTML元素的途径,是一个封装的组件,传入参数也通过在html标签中设置属性来实现,而不需要去修改JS代码。就像一个函数一样,可以到处的调用(复用),并且设置不同的属性(参数)来实现不同的功能。

在HTML中要用内置指令ng-app标记出应用的根节点,这个指令需要以属性的形式来使用,因此可以将它写到任何位置,但是写到<html>的开始标签上是最常规的做法。所有内置指令的命名空间都使用ng作为前缀,比如之前所说的ng-repeat、ng-app、ng-If等。因此我们在自定义指令命名时,为了防止命名空间冲突,不要在自定义指令前加ng前缀。

自定义的指令一般有四种表现形式:

1、作为一个新的HTML元素来使用。

<hello></hello>或者<hello/>

2、作为一个元素的属性来使用

<div hello></div>

3、作为一个元素的类来使用

<div class="hello"></div>

4、作为注释来使用

<!--directive: hello-->

下面是定义一个最简单指令的示例代码:

var app =angular.module('app',[])

app.directive('hello',function(){
    return {
       restrict:'AECM',
       template:'<button>clickme</button>'
    }
})

1、restrict[string]这个属性,主要是用来规定指令在HTML代码中可以使用什么表现形式。A代表属性、E代表元素、C代表类、M代表注释。

2、template[string or function]这个属性,规定了指令被Angular编译和链接(link)后生成的HTML标记,这个属性可以简单到只有一个HTML文本在里面,也可以特别复杂,当该属性的值为function的时候,那么该方法返回的就是代表模板的字符串。

在定义指令的时候,除了以上最基础的两个属性外,我们还会使用很多其它的属性,

1、priority[number]属性,这个属性是来规定自定义的指令的优先级的,当一个DOM元素上面有一个以上的指令的时候,就需要去比较指令的优先级了,优先级高的指令先执行。

2、terminal[boolean]属性,该参数用来定义是否停止当前元素上比本指令优先级低的指令,如果值为true,就是正常情况,按照优先级高低的顺序来执行,如果设置为false,就不会执行当前元素上比本指令优先级低的指令【9】。

参考资料

[1]https://github.com/fouber/blog/issues/10 百度工程师张云龙前端工程-基础篇

[2]http://www.cnblogs.com/fecktty2013/p/4800645.html周信达 小菜的前端编程散谈

[3]https://gf-rd.gitbooks.io/angular-2-step-by-step/content/chapters/1.2.htmlAngularJS的七个核心概念图片来源

[4]http://blog.csdn.net/rongbo_j/article/details/45438181AngularJS轻松入门(四)模块化

[5]https://segmentfault.com/a/1190000004329594#articleHeader4AngularJS核心概念之组件树 图片来源

[6]http://www.cnblogs.com/tangshiwei/p/5496268.htmlAngularJS学习笔记之依赖注入

[7]http://www.cnblogs.com/lijin1185374093/p/5938443.html走进AngularJS----服务

[8]http://angular-tips.com/blog/2013/08/watch-how-the-apply-runs-a-digest/ AngularJS Tips

[9]http://www.cnblogs.com/ww-ervin-72/p/5296788.htmlAngularJS中的指令

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容