在公司回来,明天就要开启奔波模式啦。
借此缓和的机会,总结一下在公司自学angular之后,可能会面临的一些问题。
加深一下印象。
1.angularjs中的作用域
例子:
<div ng-controller="TestCtrl"> <p>{{name}}</p> <div ng-if="show"> <input type="text" ng-model="name"> </div> </div> <script> function TestCtrl($scope){ $scope.show = true; $scope.name = 'htf'; } </script>
在这段代码中,不管input
标签内的值怎么变,p标签的内容始终不会变化。这是因为angular的ng-if
,ng-switch
,ng-include
都会隐式的创建一个新的作用域。
我们知道,js有基本数据类型和引用数据类型之分。
当我们传递一个基本类型时,js采用的是值传递,b=a,就是把栈里存放的a数值复制到b里。
当我们传递一个引用类型时,js采用的是引用传递。因为引用类型(相当于c中的结构体)是把所有的属性存放到堆中(因为我们是不能预测它的大小的),把堆的地址存放到栈中。执行b=a的时候,我们把栈中的a的地址赋值给b,这样b和a引用的都是同一块地址,同一批数据,b修改自己的属性的时候,通过a也可以访问到数据的改变。
所以,我们在angular
中不通的作用域之间传递时,尽量使用引用类型来传递。
即:
<div ng-controller="OuterCtrl"> <p>{{x}}</p> <div ng-controller="InnerCtrl"> <input type="text" ng-model="x"> </div> </div> <script> function OuterCtrl($scope){ $scope.data = {}; $scope.data.x = 'hello'; } function InnerCtrl($scope){ } </script>
注:
ng-if
,ng-switch
,ng-include
会产生新的作用域,但是ng-show
,ng-hide
就并不会产生新的作用域。不过保险起见,都是用引用类型传值吧。
看到别的大大这么说:(可能是假的,回公司证实一下)
这里还有一个区别,开发者在使用ng-if
的时候,angular是不去创建内部的dom结构的。也就是说连dom树终都没有唉~~~
但是ng-switch
和ng-hide
是加载了内部html结构,知识在display:none和display:block之间切换而已。
2.angularjs页面中不可以调用其他控制器的方法甚至js原生方法
<p>{{parseInt(55.66)}}<p>
会发现,什么也没有显示。
$scope.parseInt = function(x){
return parseInt(x);
}
就可以显示了。
angularjs是MVC架构的。题目的想法完全与MVC相违背。我们需要的方法我们可以创建服务和指令,注入到我们这个controller里,在这个controller中与页面交互。
这样可以降低耦合,而不是乱作一团。
(ps:事实上我怎么感觉vue.js的代码结构更加清晰更加简单,MVVM的强大。。。)
3.angularjs中的过滤器
http://www.tuicool.com/articles/vmmeQvj
4.factory、service 和 provider 是什么关系?
service调用的factory,factory调用的provider。
factory
把 service 的方法和数据放在一个对象里,并返回这个对象
app.factory('FooService',function(){
return {
target: 'factory',sayHello: function(){
return 'hello ' + this.target;
}
}
});
service
通过构造函数方式创建 service,返回一个实例化对象
app.service('FooService',function(){
var self = this;
this.target = 'service';
this.sayHello = function(){
return 'hello ' + self.target;
}
});
provider
创建一个可通过 config 配置的 service,$get 中返回的,就是用 factory 创建 service 的内容
app.provider('FooService',function(){
this.configData = 'init data';
this.setConfigData = function(data){
if(data){
this.configData = data;
}
}
this.$get = function(){
var self = this;
return {
target: 'provider',sayHello: function(){
return self.configData + ' hello ' + this.target;
}
}
}
});
// 此处注入的是 FooService 的 provider
app.config(function(FooServiceProvider){
FooServiceProvider.setConfigData('config data');
});
provider
创建的服务可以配置.
5. angular 的数据绑定采用什么机制?详述原理
脏检查机制。
原理就是,Angular 在 scope 模型上设置了一个 监听队列,用来监听数据变化并更新 view 。每次绑定一个东西到 view 上时 AngularJS 就会往
$digest 循环的上限是 10 次(超过 10次后抛出一个异常,防止无限循环)。
6. AngularJS 中 Controller 之间的通信
在平时的业务实现中,发现有时候后台接受的User数据会在不太能够的controller中用到。
然而我再去发送一次AJAX请求?明明数据我之前已经收到了呀。
这时候我就要通过controller之间数据通信来解决。
- 1.通过service(因为service都是单例模式的)。
- 我们对后台的交互都通过service来接收,我们只需要在不通的controller中注入同一个service即可传递数据了。
- 2.通过父controller。我们知道父controller中的引用类型传递到子controller中还是可以使用不会出错。如果父controller中数据是基本类型呢?子controller使用
$scope.$parent.data
即可。 - 3.通过事件传播 。子controller通过
$emit
向父controller传播,父controller通过$on
接收,使用$broadcast
来广播。子controller使用$on
来接收。 - 4.传递到rootscope。(没试过)
7.一个 angular 应用应当如何良好地分层?
我感觉我们的项目分层就不是太好,有些混乱。
这样怎么样呀~
- directives //指令们
- services //公共服务们
- filters //过滤器们
- common //公共组件们
- aModal
- aModal.html
- aModalCtr.js
- aModal
- tpl
- login,html
- loginCtr.js
尽量让 controller 这一层很薄。提取共用的逻辑到 service 中 (比如后台数据的请求,数据的共享和缓存,基于事件的模块间通信等),提取共用的界面操作到 directive 中(比如将日期选择、分页等封装成组件等),提取共用的格式化操作到 filter 中等等。
8. 依赖注入服务,原理
服务是单例模式的。
注入在任何controller里可以实现通信。
原理
angularjs通过传入参数的参数名,把它tostring,来获得对应的字符串,然后使用正则表达式,解析出其中的依赖项,再去以来映射中获取那个依赖,实例化,传入。
问题
myApp.controller('myCtrl',['$scope','$http',function($scope,$http){
...
}])
或显式inject
myApp.controller('myCtrl',myCtrl);
function myCtrl = ($scope,$http){
...
}
myCtrl.$inject = ['$scope','$http'];
9.angular 路由
了解 angular内置的 ngRoute 和插件 ui.router。
项目使用到的是ui.router。
ngRoute是基于url的,ui-router是基于state的(大大说的,具体实现的区别是什么呢??没有研究过)
ui.router更加强大,可以多级路由嵌套。这个在前面的博文中总结过。
使用 ui.router 能够定义有明确父子关系的路由,并通过 ui-view 指令将子路由模版插入到父路由模板的 <div ui-view></div>
中去,从而实现视图嵌套。而在 ngRoute 中不能这样定义,如果同时在父子视图中 使用了 <div ng-view></div>
会陷入死循环。
ngRoute
var app = angular.module('ngRouteApp',['ngRoute']);
app.config(function($routeProvider){
$routeProvider
.when('/main',{
templateUrl: "main.html",controller: 'MainCtrl'
})
.otherwise({ redirectTo: '/tabs' });
ui.router
var app = angular.module("uiRouteApp",["ui.router"]);
app.config(function($urlRouterProvider,$stateProvider){
$urlRouterProvider.otherwise("/index");
$stateProvider
.state("Main",{
url: "/main",templateUrl: "main.html",controller: 'MainCtrl'
})
10.angular 的缺点有哪些?
- 前面的博文有提到说,angular使用脏检查机制,遇到数据量大的情况下,会影响性能。
- html是在不停动态渲染生成的。不利于SEO。肯定更不利于移动端了。现在有一种趋势,叫做前端页面静态化。
- 前面发现,MVC限制了不同controller之间的通信是那么的困难,九曲十八弯。不过这也是MVC的特点。(我们前端,有必要把页面做得那么笨重,那么复杂吗?有时候简单清晰,没有那么多框架依赖,小而美才是我们前端工程师最应该追求的吧。)
11.如何看待 angular 1.2 中引入的 controller as 语法?
我想说我接触的项目,很多controller都是用了这种语法,我们在controller中把变量给this赋值,我们在视图中就可以访问。再也不需要绑定到$scope
上了。
这是什么原理呢?
从源码实现上来看,controllerAs 语法只是把 controller 这个对象的实例用 as 别名在 $scope 上创建了一个属性。
if (directive.controllerAs) {
locals.$scope[directive.controllerAs] = controllerInstance;
}
原来是这样!这不还是使用了$scope
么。
只是这样就避免了ng-if
等坑。
使用 controllerAs 会遇到的一个问题是,因为没有注入 $scope
,导致 $emit
、 $broadcast
、 $on
、 $watch
等 $scope
下的方法无法使用。这些跟事件相关的操作可以封装起来统一处理,或者在单个 controller 中引入 $scope
,特殊对待。
解决了一个坑又出来了一堆新坑。掀桌。
12.angularjs中$apply和$digest区别
- $apply接受一个函数,这个函数会被包装到try…catch….块中,所以有异常处理。apply会触发$rootScope实现全范围的digest circle
- $digest只能实现自身scope和子scope的 digest circle。
所以ajax请求或者timeout之后当然选择调用$apply了。