angular常见问题总结

本文引自:http://www.javashuo.com/article/p-uduvhkdb-cd.htmlhtml

这篇是对angularJS的一些疑点回顾,是对目前angularJS开发的各类常见问题的整理汇总。若是对文中的题目所有了然于胸,以为对整个angular框架应该掌握的七七八八了。但愿志同道合的通知补充内容前端

Angular 的数据绑定采用什么机制,详述原理?

脏检查机制。阐释脏检查机制,必须先了解以下问题。git

单向绑定(ng-bind) 和 双向绑定(ng-model) 的区别?

ng-bind 单向数据绑定($scope -> view),用于数据显示,简写形式是 {{}}。angularjs

二者的区别在于页面没有加载完毕 {{val}} 会直接显示到页面,直到 Angular 渲染该绑定数据(这种行为有可能将 {{val}} 让用户看到);而 ng-bind 则是在 Angular 渲染完毕后将数据显示。github

ng-model 是双向数据绑定(scope>viewandview>scope−>viewandview−>scope),用于绑定值会变化的表单元素等。web

双向数据绑定是 AngularJS 的核心机制之一。当 view 中有任何数据变化时,会更新到 model ,当 model 中数据有变化时,view 也会同步更新,显然,这须要一个监控。bootstrap

 

双向数据绑定的原理?

Angular 在 scope 模型上设置了一个 监听队列,用来监听数据变化并更新 view 。segmentfault

   每次绑定一个东西到 view 上时 AngularJS 就会往 watchwatch队列里插入一条watch,用来检测它监视的 model 里是否有变化的东西。    数组

当你写下表达式如{{ val }}时,AngularJS在幕后会为你在scope模型上设置一个watcher(表达式将被 Angular 编译成一个监视函数),它用来在数据发生变化的时候更新view。这里的watcher和你会在AngularJS中设置的watcher是同样的:浏览器

1
2
3
$scope.$watch( 'val' function (newValue, oldValue) {
   //update the DOM with newValue
});
  • 将数据附加到 Scope 上,数据自身不会对性能产生影响,若是没有监视器来监视这个属性,那个这个属性在不在 Scope 上是无关重要的;Angular 并不会遍历 Scope 上的属性,它将遍历全部的观察器。

  • 每一个监视函数是在每次 $digest 过程当中被调用的。所以,咱们要注意观察器的数量以及每一个监视函数或者监视表达式的性能。

 

 

$digest循环是在何时以各类方式开始的?

当浏览器接收到能够被 angular context 处理的事件时,digestdigest循环就会触发,遍历全部的watch,最后更新 dom。

举个栗子

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

click 时会产生一次更新的操做(至少触发两次 $digest 循环)

  • 按下按钮

  • 浏览器接收到一个事件,进入到 angular context

  • digestdigest循环开始执行,查询每一个watch 是否变化

  • 因为监视 scope.valscope.val的watch 报告了变化,所以强制再执行一次 $digest 循环

  • 新的 $digest 循环未检测到变化

  • 浏览器拿回控制器,更新 $scope. val.新值对应的 dom

 

在调用了scope.scope.digest()后,digestngclickhandlerscopeAngularJSdigest循环就开始了。假设你在一个ng−click指令对应的handler函数中更改了scope中的一条数据,此时AngularJS会自动地经过调用digest()来触发一轮digestdigest循环。当digest循环开始后,它会触发每一个watcher。这些watchers会检查scope中的当前model值是否和上一次计算获得的model值不一样。若是不一样,那么对应的回调函数会被执行。调用该函数的结果,就是view中的表达式内容(译注:诸如{{ val }})会被更新。除了ng-click指令,还有一些其它的built-in指令以及服务来让你更改models(好比ng-model,timeout)timeout等)和自动触发一次digest循环。

目前为止还不错!可是,有一个小问题。在上面的例子中,AngularJS并不直接调用digest()digest(),而是调用scope.apply()apply(),后者会调用rootScope.$digest()。所以,一轮digestdigest循环在rootScope开始,随后会访问到全部的children scope中的watchers。

一般写代码时咱们无需主动调用 applyapply或digest 是由于 angular 在外部对咱们的回调函数作了包装。例如经常使用的 ng-click,这是一个指令(Directive),内部实现则 相似 于

1
2
3
DOM.addEventListener( 'click' function  ($scope) {
   $scope.$apply(() => userCode());
});

能够看到:ng-click 帮咱们作了 applyapply这个操做。相似的不仅是这些事件回调函数,还有http、timeoutangularServicetimeout等。我听不少人抱怨说angular这个库太大了什么都管,其实你能够不用它自带的这些服务(Service),只要你记得手工调用scope.$apply。

如今,假设你将ng-click指令关联到了一个button上,并传入了一个function名到ng-click上。当该button被点击时,AngularJS会将此function包装到一个wrapping function中,而后传入到scope.scope.apply()。所以,你的function会正常被执行,修改models(若是须要的话),此时一轮$digest循环也会被触发,用来确保view也会被更新。

 

Note: scope.scope.apply()会自动地调用rootScope.rootScope.digest()。apply()functionfunctionapply()方法有两种形式。第一种会接受一个function做为参数,执行该function而且触发一轮digest循环。第二种会不接受任何参数,只是触发一轮$digest循环。咱们立刻会看到为何第一种形式更好。

 

$digest 循环会运行多少次?

$digest 循环的上限是 10 次(超过 10次后抛出一个异常,防止无限循环)。

$digest 循环不会只运行一次。在当前的一次循环结束后,它会再执行一次循环用来检查是否有 models 发生了变化。

这就是脏检查(Dirty Checking),它用来处理在 listener 函数被执行时可能引发的 model 变化。所以 digestmodeldigest循环会持续运行直到model再也不发生变化,或者digest 循环的次数达到了 10 次(超过 10 次后抛出一个异常,防止无限循环)。

当 $digest 循环结束时,DOM 相应地变化。

脏检查如何被触发?

angular 会在可能触发 UI 变动的时候进行脏检查:这句话并不许确。实际上,

脏检查是digest执行的,另外一个更经常使用的用于触发脏检查的函数apply——其实就是 $digest 的一个简单封装(还作了一些抓异常的工做)。

一般写代码时咱们无需主动调用 applyapply或digest 是由于 angular 在外部对咱们的回调函数作了包装。例如经常使用的 ng-click,这是一个指令(Directive),内部实现则 相似于

1
2
3
DOM.addEventListener( 'click' function  ($scope) {
   $scope.$apply(() => userCode());
});

angular对经常使用的dom事件,xhq事件做了封装,若是调用这些封装,就会在里面触发进入angular的digest流程,主要有如下状况:

  • DOM事件,如用户输入文本,点击按钮等,(ng-click)

  • XHQ响应事件($http)

  • 浏览器Location变动事件,即Url中hash部分变动($location)

  • Timer事件(Timeout,Timeout,interval)

  • 手动调用applyapply或digest

 

apply()apply()和digest() 的区别?

 

applyapply是scope(或者是 direcvie 里的 link 函数中的 scope)的一个函数,调用它会强制一次 digestdigest循环(除非当前正在执行循环,这种状况下会抛出一个异常,这是咱们不须要在那里执行apply 的标志)。

apply()apply()和digest() 有两个区别。

  • 1) 最直接的差别是, $apply 能够带参数,它能够接受一个函数,而后在应用数据以后,调用这个函数。因此,通常在集成非 Angular 框架(好比jQuery)的代码时,能够把代码写在这个里面调用。

  • 2) 当调用 digestdigest的时候,只触发当前做用域和它的子做用域上的监控,可是当调用apply 的时候,会触发做用域树上的全部监控。

何时手动调用 $apply() 方法?

取决因而否在 Angular 上下文环境(angular context)。

AngularJS对此有着很是明确的要求,就是它只负责对发生于AngularJS上下文环境中的变动会作出自动地响应(即,在apply()models)AngularJSbuiltinmodelviewAngularJSmodelapply()方法中发生的对于models的更改)。AngularJS的built−in指令就是这样作的,因此任何的model变动都会被反映到view中。可是,若是你在AngularJS上下文以外的任何地方修改了model,那么你就须要经过手动调用apply()来通知AngularJS。这就像告诉AngularJS,你修改了一些models,但愿AngularJS帮你触发watchers来作出正确的响应。

典型的须要调用 $apply() 方法的场景是:

  • 1) 使用了 JavaScript 中的 setTimeout() 来更新一个 scope model

  • 2) 用指令设置一个 DOM 事件 listener 而且在该 listener 中修改了一些 models

场景一

1
2
3
4
5
6
7
$scope.setMsg =  function () {
     setTimeout( function () {
         $scope.message =  'hello world' ;
         console.log( 'message:'  + $scope.message);
     }, 2000);
}
$scope.setMsg();

运行这个例子,会看到过了两秒钟以后,控制台确实会显示出已经更新的 model,然而,view 并无更新。

在 scope.getMessagescope.getMessage加入apply() 方法。

1
2
3
4
5
6
7
8
$scope.getMessage =  function () {
     setTimeout( function () {
         $scope.$apply( function () {
             $scope.message =  'hello world' ;
             console.log( 'message:'  + $scope.message);
         });
     }, 2000);
}

再运行就 OK 了。

 

不过,在 AngularJS 中应该尽可能使用 timeoutServicesetTimeout()timeoutService来代替setTimeout(),由于前者会帮你调用apply(),让你不须要手动地调用它。

场景二

实现一个 click 的指令,相似如下功能,directive 的编写以下:

1
2
3
4
5
6
7
app.directive( "inc" function () {
     return  function  (scope, element, attr) {
         element.on( "click" function () {
             scope.val++;
         });
     };
});

 

跟场景一的结果同样,这个时候,点击按钮,界面上的数字并不会增长。但查看调试器,发现数据确实已经增长了。

在 scope.val++; 一行后面添加 scope.apply();scope.apply();或者scope.digest(); 就 OK 了。

$apply() 方法的两种形式

1
2
3
4
5
6
//无参
$scope.$apply()
//有参
$scope.$apply( function (){
     ...
})

应该总使用接受一个 function 做为参数的 apply()functionapply()方法。这是由于当传入一个function到apply() 中的时候,这个 function 会被包装到一个 try…catch 块中,因此一旦有异常发生,该异常会被 $exceptionHandler service 处理。

 

想象一下若是有个 alert 框显示错误给用户,而后有个第三方的库进行一个网络调用而后失败了,若是不把它封装进 $apply 里面,Angular 永远不会知道失败了,alert 框就永远不会弹出来了。

在 AngularJS 中使用 $watch注意事项?

若是要监听的是一个对象,那还须要第三个参数

1
2
3
4
5
$scope.data.name =  'htf' ;
$scope.$watch( 'data' function (newValue, oldValue) {
     if  (newValue === oldValue) {  return ; }
     $scope.updated++;
},  true );

表示比较的是对象的值而不是引用,若是不加第三个参数 true ,在 data.name 变化时,不会触发相应操做,由于引用的是同一引用。

脏检查的范围

前面说到:angular 会对全部绑定到 UI 上的表达式作脏检查。其实,在 angular 实现内部,全部绑定表达式都被转换为 scope.scope.watch()。每一个 watchngbind="a"watch记录了上一次表达式的值。有ng−bind="a"即有scope.watch(a,callback)watch(′a′,callback),而scope.$watch 可不会管被 watch 的表达式是否跟触发脏检查的事件有关。

例如:

1
2
3
4
5
< div  ng-show="false">
   < span  id="span1" ng-bind="content"></ span >
</ div >
< span  id="span2" ng-bind="content"></ span >
< button  ng-click="">TEST</ button >

问:点击 TEST 这个按钮时会触发脏检查吗?触发几回?

首先:ng-click="" 什么都没有作。angular 会由于这个事件回调函数什么都没作就不进行脏检查吗?不会。

而后:#span1 被隐藏掉了,会检查绑定在它上面的表达式吗?尽管用户看不到,可是 scope.scope.watch('content', callback) 还在。就算你直接把这个 span 元素干掉,只要 watch 表达式还在,要检查的还会检查。

再次:重复的表达式会重复检查吗?会。

最后:别忘了 ng-show="false"。多是由于 angular 的开发人员认为这种绑定常量的状况并很少见,因此 $watch 并无识别所监视的表达式是不是常量。常量依旧会重复检查。

因此:

答:触发三次。一次 false,一次 content,一次 content

因此说一个绑定表达式只要放在当前 DOM 树里就会被监视,无论它是否可见,无论它是否被放在另外一个 Tab 里,更无论它是否与用户操做相关。

另外,就算在不一样 Controller 里构造的 scopeangularscope也会互相影响,别忘了angular还有全局的rootScope,你还能够 scope.scope.emit。angular 没法保证你绝对不会在一个 controller 里更改另外一个 controller 生成的 scope,包括 自定义指令(Directive)生成的 scope 和 Angular 1.5 里新引入的组件(Component)。

因此说不要怀疑用户在输入表单时 angular 会不会监听页面左边导航栏的变化。

如何优化脏检查与运行效率

脏检查慢吗?

说实话脏检查效率是不高,可是也谈不上有多慢。简单的数字或字符串比较能有多慢呢?十几个表达式的脏检查能够直接忽略不计;上百个也能够接受;成百上千个就有很大问题了。绑定大量表达式时请注意所绑定的表达式效率。建议注意一下几点:

  • 表达式(以及表达式所调用的函数)中少写太过复杂的逻辑

  • 不要链接太长的 filter(每每 filter 里都会遍历而且生成新数组)

  • 不要访问 DOM 元素。

一、使用单次绑定减小绑定表达式数量
单次绑定(One-time binding 是 Angular 1.3 就引入的一种特殊的表达式,它以 :: 开头,当脏检查发现这种表达式的值不为 undefined 时就认为此表达式已经稳定,并取消对此表达式的监视。这是一种行之有效的减小绑定表达式数量的方法,与 ng-repeat 连用效果更佳(下文会提到),但过分使用也容易引起 bug。

二、善用 ng-if 减小绑定表达式的数量

 

若是你认为 ng-if 就是另外一种用于隐藏、显示 DOM 元素的方法你就大错特错了。

ng-if 不只能够减小 DOM 树中元素的数量(而非像 ng-hide 那样仅仅只是加个 display: none),每个 ng-if 拥有本身的 scope,ng-if 下面的 $watch 表达式都是注册在 ng-if 本身 scope 中。当 ng-if 变为 false,ng-if 下的 scope 被销毁,注册在这个 scope 里的绑定表达式也就随之销毁了。

考虑这种 Tab 选项卡实现:

1
2
3
4
5
6
7
8
9
10
< ul >
   < li  ng-class="{ selected: selectedTab === 1 }">Tab 1 title</ li >
   < li  ng-class="{ selected: selectedTab === 2 }">Tab 2 title</ li >
   < li  ng-class="{ selected: selectedTab === 3 }">Tab 3 title</ li >
   < li  ng-class="{ selected: selectedTab === 4 }">Tab 4 title</ li >
</ ul >
< div  ng-show="selectedTab === 1">[[Tab 1 body...]]</ div >
< div  ng-show="selectedTab === 2">[[Tab 2 body...]]</ div >
< div  ng-show="selectedTab === 3">[[Tab 3 body...]]</ div >
< div  ng-show="selectedTab === 4">[[Tab 4 body...]]</ div >

对于这种会反复隐藏、显示的元素,一般人们第一反应都是使用 ng-show 或 ng-hide 简单的用 display: none 把元素设置为不可见。

然而入上文所说,肉眼不可见不表明不会跑脏检查。若是将 ng-show 替换为 ng-if 或 ng-switch-when

1
2
3
4
< div  ng-if="selectedTab === 1">[[Tab 1 body...]]</ div >
< div  ng-if="selectedTab === 2">[[Tab 2 body...]]</ div >
< div  ng-if="selectedTab === 3">[[Tab 3 body...]]</ div >
< div  ng-if="selectedTab === 4">[[Tab 4 body...]]</ div >

有以下优势:

  • 首先 DOM 树中的元素个数显著减小至四分之一,下降内存占用

  • 其次 $watch 表达式也减小至四分之一,提高脏检查循环的速度

  • 若是这个 tab 下面有 controller(例如每一个 tab 都被封装为一个组件),那么仅当这个 tab 被选中时该 controller 才会执行,能够减小各页面的互相干扰

  • 若是 controller 中调用接口获取数据,那么仅当对应 tab 被选中时才会加载,避免网络拥挤

固然也有缺点:

  • DOM 重建自己费时间

  • 若是 tab 下有 controller,那么每次该 tab 被选中时 controller 都会被执行

  • 若是在 controller 里面调接口获取数据,那么每次该 tab 被选中时都会从新加载

  • 各位读者本身取舍。

三、给 ng-repeat 手工添加 track by

不恰当的 ng-repeat 会形成 DOM 树反复从新构造,拖慢浏览器响应速度,形成页面闪烁。除了上面这种比较极端的状况,若是一个列表频繁拉取 Server 端数据自刷新的话也必定要手工添加 track by,由于接口给前端的数据是不可能包含 $$hashKey 这种东西的,因而结果就形成列表频繁的重建。

其实没必要考虑那么多,总之加上没坏处,至少能够避免 angular 生成 $$hashKey 这种奇奇怪怪的东西。

具体参看: 详解track by

 

脏检测的利弊?

不少人对Angular的脏检测机制感到不屑,推崇基于setter,getter的观测机制,在我看来,这只是同一个事情的不一样实现方式,并无谁彻底赛过谁,二者是各有优劣的。

 

你们都知道,在循环中批量添加DOM元素的时候,会推荐使用DocumentFragment,为何呢,由于若是每次都对DOM产生变动,它都要修改DOM树的结构,性能影响大,若是咱们能先在文档碎片中把DOM结构建立好,而后总体添加到主文档中,这个DOM树的变动就会一次完成,性能会提升不少。

同理,在Angular框架里,考虑到这样的场景:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function  TestCtrl($scope) {
     $scope.numOfCheckedItems = 0;
 
     var  list = [];
 
     for  ( var  i=0; i<10000; i++) {
         list.push({
             index: i,
             checked:  false
         });
     }
 
     $scope.list = list;
 
     $scope.toggleChecked =  function (flag) {
         for  ( var  i=0; i<list.length; i++) {
             list[i].checked = flag;
             $scope.numOfCheckedItems++;
         }
     };
}

若是界面上某个文本绑定这个numOfCheckedItems,会怎样?在脏检测的机制下,这个过程毫无压力,一次作完全部数据变动,而后总体应用到界面上。这时候,基于setter的机制就惨了,除非它也是像Angular这样把批量操做延时到一次更新,不然性能会更低。

 

因此说,两种不一样的监控方式,各有其优缺点,最好的办法是了解各自使用方式的差别,考虑出它们性能的差别所在,在不一样的业务场景中,避开最容易形成性能瓶颈的用法。

ng-if跟ng-show/hide的区别有哪些?

第一点区别是,ng-if 在后面表达式为 true 的时候才建立这个 dom 节点,ng-show 是初始时就建立了,用 display:block 和 display:none 来控制显示和不显示。第二点区别是,ng-if 会(隐式地)产生新做用域,ng-switch 、 ng-include 等会动态建立一块界面的也是如此。

ng-repeat迭代数组的时候,若是数组中有相同值,会有什么问题,如何解决?

会提示 Duplicates in a repeater are not allowed. 加 track by $index 可解决。固然,也能够 trace by 任何一个普通的值,只要能惟一性标识数组中的每一项便可(创建 dom 和数据之间的关联)。

ng-click中写的表达式,能使用JS原生对象上的方法,好比Math.max之类的吗?为何?

不能够。只要是在页面中,就不能直接调用原生的 JS 方法,由于这些并不存在于与页面对应的 Controller 的 scopescope中。除非在scope 中添加了这个函数:

1
2
3
$scope.parseInt =  function (x){
     return  parseInt(x);
}

{{now | 'yyyy-MM-dd'}}这种表达式里面,竖线和后面的参数经过什么方式能够自定义?

定义方式:

1
2
3
4
5
6
app.filter( '过滤器名称' , function (){
     return  function (须要过滤的对象, 过滤器参数1, 过滤器参数2, ...){
         //...作一些事情
         return  处理后的对象;
     }
});

使用方式有两种,一种是直接在页面里:

1
< p >{{now | date : 'yyyy-MM-dd'}}</ p >

一种是在 js 里面用:

1
2
// $filter('过滤器名称')(须要过滤的对象, 参数1, 参数2,...)
$filter( 'date' )(now,  'yyyy-MM-dd hh:mm:ss' );

 factory和service,provider是什么关系?

factory 把 service 的方法和数据放在一个对象里,并返回这个对象;service 经过构造函数方式建立 service,返回一个实例化对象;provider 建立一个可经过 config 配置的 service。从底层实现上来看,service 调用了 factory,返回其实例;factory 调用了 provider,将其定义的内容放在 $get 中返回。factory 和 service 功能相似,只不过 factory 是普通 function,能够返回任何东西(return 的均可以被访问,因此那些私有变量怎么写你懂的);service 是构造器,能够不返回(绑定到 this 的均可以被访问);provider 是增强版 factory,返回一个可配置的 factory。

详述angular的“依赖注入”

AngularJS 是经过构造函数的参数名字来推断依赖服务名称的,经过 toString() 来找到这个定义的 function 对应的字符串,而后用正则解析出其中的参数(依赖项),再去依赖映射中取到对应的依赖,实例化以后传入。由于 AngularJS 的 injector 是假设函数的参数名就是依赖的名字,而后去查找依赖项,那若是像下面这样简单注入依赖,代码压缩后(参数被重命名了),就没法查找到依赖项了。

1
2
function  myCtrl = ($scope, $http){
     ...}

因此,一般会使用下面两种方式注入依赖(对依赖添加的顺序有要求)。

数组注释法:

1
2
3
myApp.controller( 'myCtrl' , [ '$scope' '$http' function ($scope, $http){
     ...
}])

显式 $inject :

1
2
3
4
5
myApp.controller( 'myCtrl' , myCtrl);
function  myCtrl = ($scope, $http){
     ...
}
myCtrl.$inject = [ '$scope' '$http' ];

对于一个 DI 容器,必须具有三个要素:依赖项的注册,依赖关系的声明和对象的获取。在 AngularJS 中,module 和 $provide 均可以提供依赖项的注册;内置的 injector 能够获取对象(自动完成依赖注入);依赖关系的声明,就是上面的那两种方式。

html: {{currentDate()}} js: $scope.currentDate = function(){return new Date();} 这种写法有没有问题

有问题,时间是实时变化的,而后会一直更新数据,效率低,脏数据检查到10次以后再也不继续检查;

解决方案:可使用一个变量来接收函数调用

controller as 和controller 有什么区别,能解决什么问题?

在使用controller的时候,为控制器注入windowwindow与scope,这个时候controller中的属性与方法是属于$scope的,而使用controllerAS的时候,能够将controller定义为Javascript的原型类,在html中直接绑定原型类的属性和方法

优势:

  • 可使用 Javascript 的原型类, 咱们可使用更加高级的 ES6 或者 TypeScript 来编写 Controller ;

  • 指代清晰。在嵌套scope时,子scope若是想使用父scope的属性,只需简单的使用父scope的别名引用父scope便可。

  • 避开了所谓的 child scope 原型继承带来的一些问题(原来别名ctrl就是定义在scopecontrollerJScontrollerthismodelscope上的一个对象,这就是controller的一个实例,全部在JS中定义controller时绑定到this上的model其实都是绑定到scope.ctrl上的。使用controller as的一大好处就是原型链继承给scope带来的问题都不复存在了,即有效避免了在嵌套scope的状况下子scope的属性隐藏掉父scope属性的状况。)

  • controller的定义不依赖$scope。

  • 定义controller时不用显式的依赖scopeScopeControllerPOJOPlainOldJavascriptObjectJavaObject便使AngularJS便ObjectPOJOscope,这有什么好处呢?仔细看定义,这不就是一个普通的函数定义嘛,对!这就是好处!例子中的ScopeController就是所谓的POJO(PlainOldJavascriptObject,Java里偷来的概念),这样的Object与框架无关,里面只有逻辑。因此即使有一天你的项目再也不使用AngularJS了,依然能够很方便的重用和移植这些逻辑。另外,从测试的角度看,这样的Object也是单元测试友好的。单元测试强调的就是孤立其余依赖元素,而POJO偏偏知足这个条件,能够单纯的去测试这个函数的输入输出,而不用费劲的去模拟一个假的scope。

  • 防止滥用scopescope的watch,onon,broadcast方法。可能刚刚就有人想问了,不依赖scopewatchmodel广scope我怎么watch一个model,怎样广播和响应事件。答案是无法弄,这些事还真是只有scope能干。但不少时候在controller里watch一个model是不少余的,这样作会明显的下降性能。因此,当你原本就依赖scope使controllerasscope的时候,你会习惯性的调用这些方法来实现本身的逻辑。但当使用controlleras的时候,因为没有直接依赖scope,使用watch前你会稍加斟酌,没准就思考到了别的实现方式了呢。

  • 定义route时也能用controller as。除了在DOM中显式的指明ng-controller,还有一种状况是controller的绑定是route里定义好的,那这时能使用controller as吗?答案是确定的,route提供了一个controllerAs参数。这样在模板里就能够直接使用别名home啦。

我的以为仍是偏向于使用controller as的,固然有一点要澄清,使用contoller as并无什么性能上的提高,仅仅是一种好的习惯罢了。

 

不管定义controller时有没有直接依赖scopeDOMscope使使controllerasscope,DOM中的scope是始终存在的。即便使用controlleras,双向绑定仍是经过scope的watch以及digest来实现的。

 

请简述$compile的用法?

angularjs里比较重要但又不多手动调用的要属compileangularjstablecompile服务了,一般在写组件或指令时,都是angularjs自动编译完成的,但有时咱们可能须要手动编译,好比封装一个table组件,根据参数实现自定义渲染,增长一列复选框或者一列按钮啥的,这是就须要用到compile了。

$compile,在Angular中即“编译”服务,它涉及到Angular应用的“编译”和“连接”两个阶段,根据从DOM树遍历Angular的根节点(ng-app)和已构造完毕的 \$rootScope对象,依次解析根节点后代,根据多种条件查找指令,并完成每一个指令相关的操做(如指令的做用域,控制器绑定以及transclude等),最终返回每一个指令的连接函数,并将全部指令的连接函数合成为一个处理后的连接函数,返回给Angluar的bootstrap模块,最终启动整个应用程序。

 

先解说下angular中页面处理

ng对页面的处理过程:

  • 浏览器把HTML字符串解析成DOM结构

  • ng把DOM结构给$compile,返回一个link函数

  • 传入具体的scope调用这个link函数

  • 获得处理后的DOM,这个DOM处理了指令,链接了数

 

$compile是个编译服务。编译服务主要是为指令编译DOM元素。

编译一段HTML字符串或者DOM的模板,产生一个将scope和模板链接到一块儿的函数。

$compile用法:

1
$compile(element,transclude,maxPriority);
  • element:将要被编译和插入模板的元素或者HTML字符串

  • transclude:指令内有效的函数。Function(angular.Scope,cloneAttachFn=)

  • maxPriority:只有在指令比给定的优先级低时应用。只影响根元素,不影响子元素

1
2
3
4
5
6
7
8
9
  .controller( 'MyController' function  ($scope, $compile) {
                 // 建立编译函数
                 var  compileFn = $compile( '<p>{{appCtrl.msg}}</p>' );
                 // 传入scope,获得编译好的dom对象(已封装为jqlite对象)
                 // 也能够用$scope.$new()建立继承的做用域
                 var  $dom = compileFn($scope);
                 // 添加到文档中
                 $dom.appendTo( 'body' );
             })

经过compilehtmldomjqLitecompile服务能够编译html字符串或dom对象或jqLite对象,而后获得一个编译函数,再传入scope,就会在当前做用域进行编译,返回编译好的jqLite对象,这时就能够直接添加到文档中了(也能够先添加到文档再编译)。

编译的实质其实就是对dom对象解析,使dom对象与scope进行耦合,经过绑定能够实现数据的更新,像Vue其实也是同样的过程。

 

$compile解说推荐看《Angular中$compile源码分析

 

这篇是对angularJS的一些疑点回顾,文章的问题大可能是从网上搜集整理而来,若有不妥之处或不远被引用,请通知本人修改,谢谢!

首发于周陆军我的网站,转载注明来源:《再谈angularJS数据绑定机制及背后原理—angularJS常见问题总结

参考文章:

相关文章
相关标签/搜索