AngularJS小结

© Young 2015-07-23 21:34
Welcome to My GitHub

概念相关

1. 脏数据检查 != 轮询检查更新。

谈起AngularJS的脏检查机制(dirty-checking), 常见的误解就是认为: ng是定时轮询去检查model是否变更。
其实,AngularJS只有在指定事件触发后,才进入$digest cycle进行脏值检测:

  • DOM事件,ng-click、ng-focus、ng-change等;
  • XHR响应事件 ($http);
  • 浏览器Location变更事件 ($location);
  • Timer事件($timeout, $interval);
  • 执行$digest()或$apply();

路由相关

1. 注意路由和锚点A标签的冲突。

当你在锚点A标签上使用事件指令的时候:

/**
 * 某模板template中的部分代码
 */
<a href="#" ng-click="router();">href属性等于"#"号的A标签就是锚点A标签</a>
/**
 * 某控制器controller中的部分代码
 * 如果这里不阻止锚点A标签的默认事件,那么会出现点击后总会回到根路由的bug。
 */
$scope.router = function(e){
    e.preventDefault();//阻止默认事件
    $location.path("otherpath");//使用路由显示其它页面
}
2. AngularJS路由传参

使用AngularJS路由机制传递参数时,有几种不同的方式;

/**
 * 第一种:使用锚点A标签,在路由路径后边用问号的方式可以传递参数
 */
<a href="#/welcome/?MarkdownNote=https://github.com/newbieYoung/MarkdownNote">Welcome to My MarkdownNote</a>
/**
 * 第二种:使用$location.search方法
 */
$location.path('/welcome');//此外还要注意这么用是错误的:$location.path('/welcome?MarkdownNote=https://github.com/newbieYoung/MarkdownNote');
$location.search('MarkdownNote,'https://github.com/newbieYoung/MarkdownNote');
//上述两种方式都可以通过“$location.search().参数名称”取得传递的值。
var param = $location.search().MarkdownNote;
/**
 * 第三种:动态路由
 */
//默认路由
$routeProvider.when('/welcome/:MarkdownNote',{
    controller:welcomeController,
    templateUrl:'welcome'
});
//第三种动态路由的方法则要通过“$route.current.params.参数名称”取得传递的值。
3. ui-router相关。

使用ui-router时,第一次去某个路由时会执行controller,但是后续跳转则不会再执行controller;但是可以监听$sope的$ionicView.enter事件,让每次进入该路由时都执行一次某个逻辑(一般是初始化相关的逻辑)。

$scope.$on('$ionicView.enter', function() {
    //init something
});

过滤器相关

1. 如果过滤器需要多个参数,则使用方式应为{{ argument1 | filter:argument2:argument3:… }}。

事件相关

1. 可以在某个作用域中监听locationChangeStart事件,并通过阻止事件的默认行为来阻止路由变化和页面渲染。

控制器相关

1. 曾经遇到个看似很诡异的bug,某个controller有时会运行,有时不会,但是模版却能渲染。后来发现是有同名controller然后由于RequireJS是无序加载,而AngularJS中同名controller会有覆盖的效果,始终只有最后的controller有效,如果被期望的controller先加载则不会运行,后加载则会运行,所以就会出现上述现象。

模块相关

1. AngularJS模块的config方法中注入某些服务(比如:$rootScope等)可能会报错,但是在config方法中配置路由时在路由的resolve中注入则不会有问题。

服务相关

1. AngularJS会自动把$timeout和$interval在页面渲染(AngularJS层面的渲染,而不仅仅指浏览器DOM加载)完成之后执行。
2. AngularJS中的$location服务只能让你改变URL;不能让你重新加载页面(之所以看到页面变化则是因为URL改变导致重新路由然后页面被重新渲染)。但你需要重新加载页面或者跳转到另外的页面时,请使用更低级别的API,$window.location.href。
3. AngularJS中url地址被$location服务分为六个部分,分别是protocol、host、port、path、search、hash,前三个是只读后三个可读可写,在每在一个digest循环中URL地址每改变一次就会路由一次;另外$location服务的url()方法可以读写path、search、hash,需要注意的是写操作时path不能有#号否则会导致path被当成参数被转义。
4. AngularJS中可以在模块的config方法中通过`$locationProvider.html5Mode(true).hashPrefix(‘!’)`配置H5模式的路由,和普通路由类似只不过url的格式不一样,同时A标签的href写法也不一样。
5. AngularJS中使用$http服务发送请求时params属性中的数据会以key=value的形式添加到url中,所以就算该请求是POST请求,在YII中使用\Yii::$app->request->post依然是取不到值的;如果达到该效果则必须把数据放到data属性中(前提是正确设置了content-type)。
6. AngularJS中可以使用$http服务发送jsonp请求,如下:
/**
 * 1、当callback为JSON_CALLBACK时,请求完成会调用success,即使window中有JSON_CALLBACK函数,也不会调用该函数;
 * (原因在于callback为JSON_CALLBACK时,AngularJS会有一些处理导致最终执行的回调函数名为`angular.callbacks._0`。)
 * 2、也可以指定其它回调函数,但必须是定义在window下的全局函数;
 * 3、url中必须加上callback。
 */
var url = 'http://host:port/url?callback=JSON_CALLBACK';
$http.jsonp(myUrl).success(function(data){
    console.log(data);
});

作用域相关

1. $scope.$watch函数监听对象或者数组时会有问题,要改成监听返回结果为JSON字符串形式的该对象或数组的的匿名函数。
/**
 * 某控制器controller中的监听函数
 */
$scope.$watch(function(){
    return JSON.stringify($scope.taskList);//匿名函数返回任务列表
}, function(){
    saveTaskList($scope.taskList);//存储最新数据
    //处理标识
    for(var i=0;i<$scope.taskList.length;i++){
        var item = $scope.taskList[i];
        if(!item.done){
            $scope.isAllTaskDone = false;
            break;
        }else{
            if(i==$scope.taskList.length-1){
                $scope.isAllTaskDone = true;
            }
        }
    }
});
2. 可以在AngularJS环境中根据dom元素获得对应的$scope。
angular.element(dom).scope();

指令相关

1. 在html标签中使用ng-model定义属性时,如果是ng-model=”A.B”那么必须保证A已经被定义过了,否则报错undefined!
2. 测试select标签ng-model=”selectedModel”如果selectedModel没有初始化没有默认值则会出现下拉框第一行总是空的情况:
<!-- 没有选择值时显示有4个选项 -->
<select ng-model="selectedModel">
    <option value="0">0</option>
    <option value="1">1</option>
    <option value="2">2</option>
</select>
3. 在html中使用指令时最好使用属性声明的方式。
4. 使用ng-repeat指令时最好使用track by模型的某个主键值来提高dom更新效率。
5. 字符串也可以被ng-repeat解析,如果该字符串存在不能转换成数字或者存在相同字符时则会报错。
6. 使用ng-class指令时如果中括号判断表达式是”[a.b|filter]”的形式时会报错”Token ‘|’ is unexcepted”。

例如:

解决办法如下:

7. ng-bind是通过设置节点的textContent属性来实现的,可防止XSS攻击。
8. 使用ng-bind-html和trustAsHtml插入Html代码时我们必须自己保证代码片段的安全。
9. ngModel指令和input标签一起使用时,如果输入的值不符合input标签type属性的声明,那么ngModel双向绑定的值始终为undefined。
<input type="email" ng-model="email"></input>
//$scope.email的值始终是undefined

动画相关

1. 使用CSS3特性transition在ngIf、ngShow等指令上实现动画时,必须保证当前元素存在且display不为none,否则transition不会生效。

第三方相关

1. AngularJS的事件机制和jQuery的事件机制是不能混用的,比如用AngularJS事件指令注册的事件用jQuery的trigger方法触发时会报错$digest is already in process。
2. AngularJS的ng-src指令和微信JSSDK的wx.chooseImage接口返回的选定照片的本地ID列表冲突,具体原因是AngularJS的ng-src或者其它类似的指令对于路径或者url有安全限制,像“app://protocol”这种自定义的协议AngularJS处理时会忽视,解决办法是更改AngularJS源代码;或者使用JS原生的方法赋值。
$compileProvider.imgSrcSanitizationWhiteList(/^\s*(https?|ftp|maitto|file|tel|app):/);
3. 使用任何非Angular机制进行数据操作时,最好调用一下$scope.$apply方法。
4. 在指令里边设置一个DOM事件监听,并在该事件的回调函数中修改了一些model,那么你需要手动调用$scope.$apply方法来确保数据变化会被正确的映射到view中,而Angular自带的那些事件指令ng-click等的函数中之所以不需要我们手动调用$scope.$apply方法是因为AngularJS会将那些方法包装到一个wrapping function中,然后传入$scope.$apply,不需要我们再处理了。

优化相关

1. 初始化显示的页面中使用ng-bind进行数据绑定,后续显示的页面使用{{}}进行数据绑定。
2. 如果加载页面时暂时隐藏的内容中涉及大量的数据绑定、事件、指令等建议用ng-if因为ng-if会阻止不必要的渲染缩短页面加载时间,此外ng-if还会创建一个子作用域;反之使用ng-show或者ng-hide,ng-show和ng-hide由于是改变元素的display属性所以DOM操作少效率高。
3. 优化$scope.$watch(watchExpression, modelChangeCallback)。
  • 避免watchExpression中执行耗时操作(比如DOM操作或者日志输出console.log),因为它在每次$digest都会执行1~2次;
  • 及时移除不必要的$watch;
4. 在页面或者模版中慎用filter,如果必须要在页面或者模版中使用filter,请尽量避免执行耗时操作,理由和优化相关第三点类似,因为如果在页面或者模版中使用filter,那么该filter有可能会被执行多次。
5. 在AngularJS中在$scope中增加非简单类型属性时(对象、数组等),可以额外在声明一个var变量,在之后的代码中就可以使用声明的var变量,来减少属性查找和代码量。
var arr = $scope.arr = [1,2,3,4];
console.log($scope.arr.length);//4
arr.push(5);//等同于$scope.arr.push(5);
console.log($scope.arr.length);//5

其它

1. input标签的ng-model和value属性互不相关
<input type="text" ng-model="inputModel" value="inputModel"/>

第一次初始化时value属性无效,初始化后在AngularJS代码中设置input.value=’inputModel’能改变input显示的值,但是不会影响ng-model。

2. 在AngularJS中使用表单

时要注意,如果表单form有name属性则表示该表单是AngularJS的一种内置指令,此时如果作用域$scope中某一属性和该表单的名字一样则谁先初始化(定义)谁就有效,另一个被覆盖。

3. 如果在使用AngularJS的过程中浏览器报错”$injector-modulerr”一般情况下模块加载失败,需要检查一下路径的配置情况,或者检查一下是否在代码中使用了未加载模块的相关内容。

4. 不要忘了在scope的销毁事件中关闭timeout和interval,虽然AngularJS会在scope销毁的时候关闭和该scope相关的事件,但是AngularJS并不会自动关闭interval和timeout。
5. 在AngularJS中有一些操作必须放到页面所有内容都渲染完成之后再去执行,比如获取元素在页面中的位置,因为双向绑定内容或者指令编译时可能会影响页面结构。

2 thoughts on “AngularJS小结

发表评论

电子邮件地址不会被公开。 必填项已用*标注