Angular简介(大神可略过)

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

1.改变一下,就请求一下后端,例如php,然后后端重新返回一个更新好的页面,当然这种方法很傻,改变一点小数据就请求后端,的确太傻(由于前端小白,我之前就用这种方式做了一个小网站,后来接触到angular才发现自己太傻);

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

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

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

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

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

一般处理数据:

angular:

引用自: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文件)

参考自:

angular原理及模块简介的更多相关文章

  1. Angular 从入坑到挖坑 - 模块简介

    一.Overview Angular 入坑记录的笔记第七篇,介绍 Angular 中的模块的相关概念,了解相关的使用场景,以及知晓如何通过特性模块来组织我们的 Angular 应用 对应官方文档地址: ...

  2. Python::OS 模块 -- 简介

    OS 模块简介 OS模块是Python标准库中的一个用于访问操作系统功能的模块,OS模块提供了一种可移植的方法使用操作系统的功能.使用OS模块中提供的接口,可以实现跨平台访问.但是在OS模块中的接口并 ...

  3. Qt5模块简介

        原文链接:Qt5 模块简介 无意中看到这篇文章,虽然讲的不是经常用的东西,但是看了这篇文章之后,可以对qt有个大致的了解,能够清晰的知道自己想要什么,应该关注那一部分,学习了,相信以后会又很大 ...

  4. Python 的 six模块简介

    Python 的 six模块简介 six : Six is a Python 2 and 3 compatibility library Six没有托管在Github上,而是托管在了Bitbucket ...

  5. signal模块简介

    signal模块简介 最近在看Linux signal 相关内容,signal可以被用来进程间通信和异步处理.Python标准库提供了signal包可以用来处理信号相关.这里讨论的是Unix系统中Py ...

  6. socketserver模块简介

    1. socketserver模块简介 在python的socket编程中,实用socket模块的时候,是不能实现多个连接的,当然如果加入其 它的模块是可以的,例如select模块,在这里见到的介绍下 ...

  7. Qt 学习之路 2(6):Qt 模块简介

    Home / Qt 学习之路 2 / Qt 学习之路 2(6):Qt 模块简介  豆子  2012年8月26日  Qt 学习之路 2  20条评论 Qt 5 与 Qt 4 最大的一个区别之一是底层架构 ...

  8. Python logging 模块简介

    Table of Contents 1. Logging 模块 1.1. 简介 1.2. 简单输出日志 1.3. 输入日志到文件 1.4. 几个基本概念 1.4.1. loggers 1.4.2. h ...

  9. collections模块简介

    collections模块简介 除python提供的内置数据类型(int.float.str.list.tuple.dict)外,collections模块还提供了其他数据类型,使用如下功能需先导入c ...

随机推荐

  1. minidump-DMP文件的生成和使用

    转载地址点击打开链接 1.生成dmp的程序 #include  <dbghelp.h> #pragma comment(lib,  "dbghelp.lib") //设 ...

  2. Mybatis Laz-Load功能实现代码赏析(原创)

    对于Mybatis 拥有的Lazy Load(有中文翻译成延迟加载)功能,应该很同学都有听说过,今天主要与大家一起来解读一下Mybatis在Lazy Load功能的实现的代码.Lazy Load实现的 ...

  3. L2-020. 功夫传人(dfs+vector 或者 邻接矩阵+dij+优先队列)

    L2-020. 功夫传人 时间限制 400 ms 内存限制 65536 kB 代码长度限制 8000 B 判题程序 Standard 作者 陈越 一门武功能否传承久远并被发扬光大,是要看缘分的.一般来 ...

  4. 使用jmx4perl和j4psh接管Jolokia

    在ActiveMQ的API中,内置了Jolokia . 可以使用jmx4perl来安装: $ perl -MCPAN -e shell Terminal does not support AddHis ...

  5. 2016.3.7 Word2007编号设置

    1.点击下图红圈出的下拉箭头 2.点击下图新建样式按钮 3.在弹出窗口中,设置名称AAA(方便稍后的查找修改),样式类型改为列表,点击编号 4.在弹出的窗口中以此设置各级标题的编号样式,保存后设置成功 ...

  6. delphi 面向对象实用技能教学二(封装)

    面向对象编程手法,是一项综合技能,单独把谁拿出来说都不合适.本次重写 TSimpleThread ,使其能在 D7 下运行. 基于 TSimpleThread ,重磅推出 TSimpleUI.ExeP ...

  7. QT5提示can not find -lGL的解决方法

    这是由于 Qt5.0 默认将OpenGL加入了工程,但是在机器上没有安装OpenGL,所以jonas只需要在机器上安装OpenGL即可 .   安装建立基本编译环境 首先不可或缺的,就是编译器与基本的 ...

  8. Solaris Tips: Repairing the Boot Archive (ZT)

    http://www.seedsofgenius.net/solaris/solaris-tips-repairing-the-boot-archive 注意以下是系统盘非镜像情况下的操作,如果系统盘 ...

  9. 问题:Oracle 树形遍历;结果:使用oracle进行遍历树操作

    使用oracle进行遍历树操作   1:首先数据库中表必须是树形结构的 2:super_department_id 为 department_id 的父节点编号 3:以下语句的执行结果是:depart ...

  10. S2SH整合所需jar包及其详解

    转自:https://blog.csdn.net/vvvac1314/article/details/44002205 struts2所必须的jar包五个:struts2-core-2.1.6.jar ...