angular原理及模块简介

Angular简介(大神可略过)php

Angular是一个强大的前端框架,其强大之处主要是能够把静态页面与动态数据绑定起来。平时咱们看到的网页界面上面的数据都是固定,但若是咱们要变化这些数据,例如我在一个文本框输入,要实时改动一个文本,肿么破。这时候有两种方法(我只想到两种,求大神告知更多):html

1.改变一下,就请求一下后端,例如php,而后后端从新返回一个更新好的页面,固然这种方法很傻,改变一点小数据就请求后端,的确太傻(因为前端小白,我以前就用这种方式作了一个小网站,后来接触到angular才发现本身太傻);前端

2.经过js改写DOM(for 小白:这里的DOM能够理解为html,但官方称呼叫document object model,小白我之前老是不知道这是啥),js最初的document类就能够干这事儿,后来出现了一个jquery(js的一个强大的库),也能够方便的改写DOM,对于这些前端就能够知道的数据就不用请求后端了。jquery

jquery也只是一个库,提供了一些简单改变DOM的方法,对于简单的小工程来讲也够了。可是对于比较大的工程,考虑的不只是功能的实现,还包括可维护可扩展,这就须要MVC模式了(for小白:至于啥是MVC,能够看我介绍spring框架那篇博客里面)。若是只用jquery,view的逻辑会和c,m的逻辑混在一块儿,不便于维护,例如你在文本框里数据了一个东西,你得用写代码去获取这个值,而后作处理,或者你的某个值改变了,你还得写代码去更新一下view,而angular就是提供这样一个解决方案的框架(后面还会有介绍感受angular的强大)。git

Angular里面的html文件就是view,叫模板(template),当你的数据变化须要改变模板的时候,不用再js代码里面去改变,你能够什么都不作,由于angular神奇的地方就是把模板与数据绑定(data binding),当数据改变的时候模板自动就变了,你的view变了(在文本框输入东西了)也会自动反应到你的数据上面,这就是双向绑定。在angular的理念里面,模板就是一副素描画,数据就是颜色,你想作完这幅画,只须要向模板填充你想要颜色就好了(也就是填充你的数据),例以下面这个例子,你的输入自动显示到界面上angularjs

http://www.runoob.com/try/try.php?filename=try_ng_introgithub

你只须要专一你的数据和模板就够了,他们之间怎么填充,angular把这些作好了,也就是剥离了view层对contorller,mdoel层的影响,下面就是angular官方给出的区别web

通常处理数据:面试

angular:spring

引用自:https://docs.angularjs.org/guide/databinding

简单来讲就是你用angular了数据和view自动双向绑定,不用你再代码中去更新,不用angular你还要本身写代码在view变了时候去更新数据,在数据变了的时候去更新view。

 

angular原理

angular是基于js的一个框架,首先须要了解一下js的工做原理。

JS原理

浏览器里面有一个事件队列(event queue),用户触发啥事儿,或者网络请求,延时操做(例如定时器之类),都是一个event,浏览器会轮训这些事件,而后调用这些回调(这里的回调简单来讲能够理解为触发一个函数),而后就进入JavaScript的环境中执行(JavaScript context),在这里面能够改变数据,操做DOM(也就是html结构),而后再退出JavaScript环境,又进入浏览器环境,而后浏览器根据以前的改动从新绘制界面,这就是个一个流程。

下图为angular 官方解释(引自https://docs.angularjs.org/guide/scope  )

angular原理

           angular的运行就是在JavaScript context里面本身实现了一套环境,叫作angular环境(angular context),非angular那部分环境叫经典环境(classic context),

在angular context里面也有一个队列,这个队列里面是watch列表,列表里面装的就是那些被监听的变量,包括那些进行数据绑定的变量(也就是和view进行绑定的那些)。若是用户改变了一个绑定了数据的view,这时候会触发一个angular函数$apply(也就是把这个event放入了event queue,而后轮训到这个的时候就触发了),而后把这个改变的值更新进绑定的那个变量,再开始调用一个digest的函数,digest就是用来轮训这个watch列表,看这个列表中的指是否变更,若是有变更就变更改写相应的DOM(不用angular就要本身写这部分代码,若是你有100个变量,你就要写100个这种改动,并且若是之后有啥变更,还得本身去重构)。

关于angular原理机制的一些参考:

http://www.cnphp6.com/archives/64167

http://www.tuicool.com/articles/fAfiMv

这里还有两点注意,

1.angular会至少轮训两遍watch列表,为啥?由于第一次轮训可能在改写DOM的时候可能会触发其余watch列表里面的变量变化,这时候还会再轮训,直到连续两次轮训的变量再也不变化。因此若是你有两个变量的变化是相互影响的,就是A变了触发B变,B变了触发A变,这样会引发死循环,angular好像是在轮训5次(或者是10次,具体我忘了),若是还发现值没有稳定,就会报错(我曾经就干过,界面忽然卡死,整个浏览器都卡死了,好不容易打开控制台看,所有是angular轮训报的错,angular的轮训直接卡死了整个浏览器)。

2.另外还有一点,关于效率问题,有人提出来angular这样无差异轮训可能会影响性能,可是angular的创始人给了解释,人能在一个页面上最多就能看200个元素,在一个web页面上面不会有这么多的元素绑定数据,若是绑定这么多元素须要实时更新,那属于网页设计的问题(引自stackoverflow,具体网址找不到),因此并不用担忧轮训的效率问题,若是真的有效率问题,说明网页自己可能存在问题(在豆瓣上看到一篇帖子,一我的用angular测了500个ngModel绑定的页面,很卡,因此对于没必要要的绑定,最好不要绑)

 

angular组件

Controller

           Controller是angular一个重要的组件,基本用angular必定会用到controller。Controller顾名思义,用来控制的,是MVC中的C,逻辑控制。在angular里面,controller是一个JavaScript的构造函数,这个函数有两个做用,初始化scope,还有就是增长方法(add behavior)。

引自:https://docs.angularjs.org/guide/controller

这里稍微简单解释一下scope(后面会说一下),scope是一个对象(object)能够理解为是链接view和controller的一个桥梁,scope的属性中有一些值,有一些方法(behavior),在html中能够直接访问到,以下图,ng-click里面的那些方法都是scope的一个属性,还有显示出来的那些值。在scope里面初始化以后,在view(也就是html中,angular官方叫作template)里面就就能够访问到这些值,也能够触发这些方法,这也就是angular数据双向绑定的具体使用(很简单吧,不用写一堆jquery了)。

引自:https://docs.angularjs.org/guide/controller

           因此controller的做用就在于上面说的,初始化scope和为scope增长方法,同时angular官方也给出了一些不建议使用的方式(以下图),由于这样操做基本上都有更好的方式

引自:https://docs.angularjs.org/guide/controller

Tips:

1.        angular在1.2版本后多了一个controlleras的语法,这个语法容许为controller起个别名(有木有感受像sql里面的as),以下图

若是不用这个语法,须要在controller这个函数里面依赖注入(Dependency Injection,后面也会介绍到,如上面那个例子所示,在函数的参数里面写个$scope)。若是用这个语法就不用了,二者的不一样就在于不用controller as这个语法,html经过访问scope的属性来访问数据,因此要把给html访问的数据写进scope的属性,若是用controller as,整个controller这个实例(例子中的demo)会做为scope的一个属性,例如html要访问一个data属性的值

Controller as 访问的是scope.demo.data

Controller 访问的是scope.data

2.        controller继承,每一个controller继承实际上是scope的继承,能够简单理解为JavaScript的继承,具体能够看我另外一篇博客(若是有时间发的话T^T),或者是angular官方文档上面说的。在这里就是若是子controller里面没有指定这个数据,就会用父类的,若是指定了就用子类的,可是要注意若是是改变model里面的值,有可能改父类的值(下面还会介绍这个概念)

 

 

Service

Service能够理解为MVC结构中的M层,来处理具体的业务逻辑,最理想的代码就是在view里面触发了controller中的函数,而后controller来调用model里面具体的处理,而后model返回给controller改scope的数据,反应在view上面。Service就是这个做用,在angular里面,service有两个特色

1.        懒加载(lazy loading):只有在须要用的时候(也就是在其余service,filter,directive或者controller里面依赖注入的时候才会生成这个service实例)

2.        单例模式(singleton):service在angular里面是单例(singleton),只在第一次被注入的时候建立实例,而后存在cache里面,等须要的时候(也就是另外的依赖注入的时候),从cache里面取出。因此service的生命周期只要建立以后,除非app退出,不然一直都有这个实例。不能销毁(我还没找到一种手动销毁的办法,事实上在网上查了一些须要销毁的例子,其实均可以用其余方法来作,不必定非要销毁这个service实例)

http://stackoverflow.com/questions/32781488/how-to-destroy-an-angular-factory-instance

 

PS:若是在使用过程当中须要多例的样子,能够本身稍微改动一下,把service当作一个factory模式,返回各类须要的实例。能够参考下面的链接(两个例子其实同样,只是在service的写法上有点不一样而已,另外提示连接里面的网页在embed标签栏里面能够看到样式)

http://jsfiddle.net/rbdmjLok/3/

http://jsfiddle.net/jeremylikness/rbdmjLok/

  • 注册service

            在官方给出的developerguide里面主要介绍了两种方式,factory模式和provider模式,但其实还有一种service模式,因此总共有三种:

1.        factory:angular里面比较经常使用的一种方式,注册一个function,这个function在生成实例的时候会被调用到,这个函数返回一个service实例(要本身写return的),因此只要把本身须要的service写成一个obj,在这个obj里面定义要的方法和值,而后再最后return一下这个obj就能够了,以下图

2.        service:service注册就更简单了,至关于对factory作了一层封装。只要在service里面写须要的方法和值就好了,不须要return,以下图

3.        provider:provider是angular里面注册service最底层的方式,不管是service方式注册仍是factory注册,其实底层都是用的provider这种方式。在provider里面有一个$get的属性,这个属性是一个函数,这个函数就是factory里面咱们写的那个函数,用service那种写法在这里就是new 一个service的那个function赋给它(js里面function也能够看作一个对象的,因此等于直接new了一个对象给$get),angular就是经过在依赖注入(后面会介绍)的时候,调用这个函数得到一个实例。

若是一个service只有在实例化它以前才知道一个配置,例如读文件或者网络返回的一些动态配置,这种就不能在代码里面写死,须要传参数给service初始化(有点相似于Java,C++里面的带参数的构造函数),这时候就须要在provider里面配置。因此provider就是在service初始化前对service作一些配置的组件(因此叫provider嘛),在provider里面留一个接口(也就是留一个函数),等获取到须要的配置的时候调provider的这个接口,就能够设置service的参数。注意:这里的provider只是提供了这样一种方法,能够把它理解为一种工具,config才是调用provider的东东。至于为啥不直接用provider调用,而还要加个config,我一开始想不通,后来问了我boss,他认为这只是为了好理解,让人一看就知道是配置。后来我也想了一下,应该也是为了方便统一配置。若是直接调provider里面建,要么分开写,要么还要本身写一个function在里面配置,angular大概应该是为了提供一个统一而且方便理解的接口吧(我的理解)。具体例子以下(注意:在provider里面写的名字,在用的时候会在名字后面加一个provider,例如例子中写的User,但实际调用的那个provider是UserProvider)

 

在使用以上三种哪一种方式,官方文档彷佛没有给出啥太明显的建议,我的是这样认为的,service更倾向因而一种服务,factory倾向于一个工具,provider是须要对这个service作一些配置。下面的博客写得很好,里面也有介绍啥时候用哪一种模式,附上一些参考。

https://my.oschina.net/tanweijie/blog/295067(上面几张图引用于此)

https://docs.angularjs.org/guide/services

http://stackoverflow.com/questions/15666048/angularjs-service-vs-provider-vs-factory?rq=1

 

scope

           scope是链接controller和view的桥梁,angular官方是这样描述的:Scopeis the glue between application controller and the view(如上面的例子能够看出)

  • scope对外api:

scope主要提供三个对外API(官方文档中developer guide里面只说起了两个)

1.watch:

监听model是否发生了变化,注意这里的watch提供三种api监听

(1)(scope.$watch(watchExpression, listener)) :只监听对应的值或者reference是否变化,若是变化就触发注册的回调函数(也就是那个listener)

(2)(scope.$watchCollection(watchExpression, listener)) :监听对应的值或者reference以及集合里是否发生变化(例如集合增长或者减小,可是不包括集合里面的值变化)

(3) (scope.$watch (watchExpression, listener, true)):监听对应的值或者reference以及集合里是否发生变化而且还包括里面的值是否发生变化,下图能够比较清晰的看出其中的区别

2.apply:

在angular context外发布model变化的消息(PS:若是在angular context外变化angular是不会更新界面的,例如用setTimeOut这种方式来更新model,由于setTimeOut只是把一个event放入了队列里面,不会立刻执行,等到执行注册timeout的这个function的时候,若是是彻底和angular无关的,也就是没有用到angular的一些内置命令,这是不会触发进入angular context的,因此这时候的运行彻底就是在angular context外,因此即便更新model的数据,也不会在view上面显示出来,因此要在外面更新通常要本身调用一下apply,具体例子能够参考下面给的那个博客。通常在ng开头的命令中和angular自带的一些service里面都会自动调用apply,因此咱们不须要去调用)

3.digest:

这个在angular官方文档中没有列出来,但其实也是能够直接调用的,官方应该是不推荐这样作。调用apply就会调用digest,digest会轮训那些watches(注册了监听的那些值的列表),若是发现值变化了会调用watch注册的那个function来进行一些处理,能够理解为apply->digest->watch

参考自:http://www.cnphp6.com/archives/64167

  •  scope种类

           scope分为两种,一种是child scope,一种是isolatescope,前者是按照相似DOM结构的继承关系,后者是彻底独立的(通常用在directive中,由于directive通常是脱离上下文,可以单独使用的,例如要作一个通用的列表,在用的时候只须要传个列表值进来就能够了,这种和上下文无关,因此通常是独立的,就相似于Android里面的adapter同样)

  • scope继承

           对于child scope的继承,就和JavaScript的继承差很少,简单来讲就是若是子scope中没有的属性,会去父scope去找,一层一层去找.

           若是这时候想赋值,不会改到父scope中的属性,例如parentScope.a= 1,若是childScope中没有指定a那么childScope.a也是1,可是若是这时候赋值childScope.a = 2,这时候parentScope.a仍是1,为啥,由于那个赋值语句对于JavaScript不是一个改变值的语句,是为childScope建立了一个a的属性值等于2,因此父parentScop不受影响。可是若是属性是model(也就是对象)就不同了,觉得访问model的时候传的reference(这里和C++是不同的,Java和JavaScript里面都是把类做为reference传,C++是经过拷贝构造函数拷贝一份,除非修改拷贝构造函数,不然默认是传值),例如:parentScope.a.value = 1,若是childScope没有指定,那么childScope.a.value也是1,这时候赋值childScope.a.value = 2,那么这时候父parentScope.a.value也是2。为啥,由于childScope.a是访问parentScope的属性(若是childScope里面没有指定,注意这个前提),因为a是个model(对象),因此访问的是地址(reference),这时候a.value=2就是对这个地址的值进行了改写,因此parentScope也会被改变。固然若是先把childScope.a=newA,这样childScope指向的就不是parentScope的了,这时候再改a的值就不会影响到parentScope了。建议能够在本身在console里面试试(JavaScript中function其实就是类,个人另一篇博客也介绍了关于JavaScript和angular的继承关系)

参考自:

https://github.com/angular/angular.js/wiki/Understanding-Scopes

https://docs.angularjs.org/guide/scope

  • 追踪scope

           这是angular官方给出的怎么在view中调试scope,也就是看scope当前的一些值

           其实还有另外一个方法,也就是咱们在项目中用到的一个方法,设一个全局变量,而后再每一个controller里面都把scope赋值给这个全局变量,这样能够在console里面从这个全局变量里面看到想追踪的scope的值了。可是注意:若是这个全局变量只是为了调试,不要在代码中使用这个全局变量,也就是不要读取,由于这个存在只是做为调试使用的,是随时会去掉的一个东西,若是有代码逻辑依赖这个全局变量的值,在去掉以后会致使错误的。因此不要让代码依赖一个随时会去掉的变量。

  • Scope事件分发

           这个在项目中咱们基本没用过,但angular提供了这个机制,emit和broadcast(作Android的同窗应该对这个单词比较有感受吧,但其实这相似于Android里面的事件传递机制event dispatch,例如touchEvent和clickEvent这类,但angular这个彷佛不存在消费,由于项目没用这个,因此也没仔细考证,求大神告知)

emit:

释放事件,当前scope和父scope均可以收到这个事件,若是在对应的scope里面有注册这个scope的回调,就会调用这个回调函数。

Broadcast:

发布事件,当前scope和子scope都会收到这个事件,若是在对应的scope里面有注册这个scope的回调,就会调用这个回调函数。

具体例子可参考

https://docs.angularjs.org/guide/scopeScope Events Propagation部分

 

 

DependencyInjection(依赖注入)

           依赖注入是在不少编程语言和框架中都会说起的一个东西。其实也很好理解,首先什么是依赖,A模块(例如类,方法),须要用到B模块中的东西,这时候就说A对B有依赖,例如A类里面有个add的方法,在B类里面须要用到这个这个add的方法,就是B对A有依赖。这时候就须要注入(其实注入也能够理解为初始化这种意思),在Java里面可能就须要new 一个A,在angular里面,就直接用函数参数这种形式来写,可是要先在其余地方定义这个依赖的类,就是要定义一个service(如上面提到的service那部分),而后再把这个service做为一个函数参数传进来。这样就至关于new了这样一个对象。具体能够参考上面service部分的例子或者angular 官方的例子

依赖注入写法在angular中有三种:

1.Inline ArrayAnnotation:

           在中括号里面用单引号写上,而且在function的参数里面写上,并且要注意顺序一致

 

2.$inject PropertyAnnotation

           用$inject来写,同时也要注意参数顺序一致

3.ImplicitAnnotation

           只在function的参数里面写,最简单的一种写法,可是也是angular官方不推荐的。由于这种写法在代码混淆中会出问题,固然也有一个工具解决这个问题,这里就不说起了,详见angular官方文档https://docs.angularjs.org/guide/di

另外angular还提供一种严苛模式(Android里面其实也有一种严苛模式,可是和angular这个不一样,Android的同窗不要弄混了),不容许Implicit Annotation,一旦用了Implicit Annotation就会报错,这里也就不介绍了,详见angular官方文档同上。另外关于解决依赖的问题,其实有三种方式(以下图),但angular认为前两种方式很差,由于前两种要本身编码,比较麻烦,特别是在单元测试的时候,因此才用第三种(spring框架也是用的这种方式),angular里面主要就是靠$injector来建立和追踪依赖,因此减轻了开发者的负担。更多详情参考angular官方文档,这里就不说起了。

 

template

           angular里面的template其实就是html,在angular中,能够用下面四种方式来控制模板(html)的显示(如图),都比较简单,看看例子就知道了,这里就不说起了。angular建议若是是简单的app,能够把全部html写在同一个html文件中,而后用directive这些来控制(通常就是index.html),若是比较复杂一点的app,能够把不一样的view(也就是html)放在同一个page里面,可是把各自view定义在不一样的html文件中,而后经过引用的方式在加载在这个page中(咱们的项目就是经过这种方式来作的,其实也能够认为是一个index.html,可是里面的不少view都来自于其余html文件)

参考自:

相关文章
相关标签/搜索