使用Angular.js开发的注意事项
前言
近期一直在玩Angularjs,不得不说,相对于Knockout,Angularjs这一MVVM框架更强大,也更复杂,各种教程网上到处都是,不过真正用到项目的时候会遇到各种坑。
一、ng-repeat
ng-repeat用于标识某个elem需要重复输出,同时重复输出的内容需为唯一
<divng-app="app"ng-controller="control">
<h3ng-repeat="contentinrepeatContent">ng-repeat:{{content}}</h3>
</div>
letapp=angular.module("app",[]);
app.controller("control",($scope)=>{
//输出李滨泓
$scope.repeatContent=["李","滨","泓"];
//下面存在两个“泓”,会报错
//$scope.repeatContent=["李","滨","泓","泓"];
})
二、provider,service,factory之间的关系
factory
factory很像service,不同之处在于,service在Angular中是一个单例对象,即当需要使用service时,使用new关键字来创建一个(也仅此一个)service。而factory则是一个普通的函数,当需要用时,他也仅仅是一个普通函数的调用方式,它可以返回各种形式的数据,例如通过返回一个功能函数的集合对象来将供与使用。
定义:
letapp=angular.module("app",[]);
//这里可以注入$http等Provider
app.factory("Today",()=>{
letdate=newDate();
return{
year:date.getFullYear(),
month:date.getMonth()+1,
day:date.getDate()
};
});
使用注入:
app.controller("control",(Today)=>{
console.log(Today.year);
console.log(Today.month);
console.log(Today.day);
});
service
service在使用时是一个单例对象,同时也是一个constructor,它的特点让它可以不返回任何东西,因为它使用new关键字新建,同时它可以用在controller之间的通讯与数据交互,因为controller在无用时其作用域链会被销毁(例如使用路由跳转到另一个页面,同时使用了另一个controller)
定义:
letapp=angular.module("app",[]);
//这里可以注入$http等Provider
//注意这里不可以使用arrowfunction
//arrowfunction不能作为constructor
app.service("Today",function(){
letdate=newDate();
this.year=date.getFullYear();
this.month=date.getMonth()+1;
this.day=date.getDate();
});
使用注入:
app.controller("control",(Today)=>{
console.log(Today.year);
console.log(Today.month);
console.log(Today.day);
});
provider
provider是service的底层创建方式,可以理解provider是一个可配置版的service,我们可以在正式注入provider前对provider进行一些参数的配置。
定义:
letapp=angular.module("app",[]);
//这里可以注入$http等Provider
//注意这里不可以使用arrowfunction
//arrowfunction不能作为constructor
app.provider("Today",function(){
this.date=newDate();
letself=this;
this.setDate=(year,month,day)=>{
this.date=newDate(year,month-1,day);
}
this.$get=()=>{
return{
year:this.date.getFullYear(),
month:this.date.getMonth()+1,
day:this.date.getDate()
};
};
});
使用注入:
//这里重新配置了今天的日期是2015年2月15日
//注意这里注入的是TodayProvider,使用驼峰命名来注入正确的需要配置的provider
app.config((TodayProvider)=>{
TodayProvider.setDate(2015,2,15);
});
app.controller("control",(Today)=>{
console.log(Today.year);
console.log(Today.month);
console.log(Today.day);
});
三、handlebars与angular符号解析冲突
场景:
当我使用node.js作为服务端,而其中使用了handlebars作为模板引擎,当node.js对某URL进行相应并render,由于其模板使用{{}}作为变量解析符号。同样地,angular也使用{{}}作为变量解析符号,所以当node.js进行render页面后,如果{{}}内的变量不存在,则该个区域会被清空,而我的原意是这个作为angular的解析所用,而不是handlebars使用,同时我也想继续使用handlebars,那么此时就需要将angular默认的{{}}解析符号重新定义。即使用依赖注入$interpolateProvider进行定义,如下示例:
app.config($interpolateProvider=>{
$interpolateProvider.startSymbol('{[{');
$interpolateProvider.endSymbol('}]}');
});
四、ng-annotate-loader
ng-annotate-loader应用于webpack+angular的开发场景,是用于解决angular在进行JS压缩后导致依赖注入失效并出现错误的解决方法
安装
$npminstallng-annotate-loader--save-dev
配置
//webpack.config.js
{
test:/\.js?$/,
exclude:/(node_modules|bower_components)/,
loader:'ng-annotate!babel?presets=es2015'
},
五、双向数据绑定
当我们使用非Angular自带的事件时,$scope里的数据改变并不会引起$digest的dirty-checking循环,这将导致当model改变时,view不会同步更新,这时我们需要自己主动触发更新
HTML
<div>{{foo}}</div>
<buttonid="addBtn">go</button>
JavaScript
app.controller("control",($scope)=>{
$scope.foo=0;
document.getElementById("addBtn").addEventListener("click",()=>{
$scope.foo++;
},false);
})
很明显,示例的意图是当点击button时,foo自增长并更新View,但是实际上,$scope.foo是改变了,但是View并不会刷新,这是因为foo并没有一个$watch检测变化后$apply,最终引起$digest,所以我们需要自己触发$apply或者创建一个$watch来触发或检测数据变化
JavaScript(使用$apply)
app.controller("control",($scope)=>{
$scope.foo=0;
document.getElementById("addBtn").addEventListener("click",()=>{
$scope.$apply(function(){
$scope.foo++;
});
},false);
})
JavaScript(使用$watch&$digest)
app.controller("control",($scope)=>{
$scope.foo=0;
$scope.flag=0;
$scope.$watch("flag",(newValue,oldValue)=>{
//当$digest循环检测flag时,如果新旧值不一致将调用该函数
$scope.foo=$scope.flag;
});
document.getElementById("addBtn").addEventListener("click",()=>{
$scope.flag++;
//主动触发$digest循环
$scope.$digest();
},false);
})
六、$watch(watchExpression,listener,[objectEquality])
注册一个listener回调函数,在每次watchExpression的值发生改变时调用
watchExpression在每次$digest执行时被调用,并返回要被检测的值(当多次输入同样的值时,watchExpression不应该改变其自身的值,否则可能会引起多次的$digest循环,watchExpression应该幂等)
listener将在当前watchExpression返回值和上次的watchExpression返回值不一致时被调用(使用!==来严格地判断不一致性,而不是使用==来判断,不过objectEquality==true除外)
objectEquality为boolean值,当为true时,将使用angular.equals来判断一致性,并使用angular.copy来保存此次的Object拷贝副本供给下一次的比较,这意味着复杂的对象检测将会有性能和内存上的问题
七、$apply([exp])
$apply是$scope的一个函数,用于触发$digest循环
$apply伪代码
function$apply(expr){
try{
return$eval(expr);
}catch(e){
$exceptionHandler(e);
}finally{
$root.$digest();
}
}
使用$eval(expr)执行expr表达式
如果在执行过程中跑出exception,那么执行$exceptionHandler(e)
最后无论结果,都会执行一次$digest循环
总结
以上就是这篇文章的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。