最近几个月频繁的跟AngularJS打交道,对于web应用开发来说Angular真的是一个神奇的框架,但是没有东西是完美的,在这篇文章里我会把我的感悟罗列出来,希望可以产生共鸣(前提是你对Angular已经有所了解)。
UI的闪烁
Angular的自动数据绑定功能是亮点,然而,他的另一面是:在Angular初始化之前,页面中可能会给用户呈现出没有解析的表达式。当DOM准备就绪,Angular计算并替换相应的值。这样就会导致出现一个丑陋的闪烁效果。
上述情形就是在Angular教程中渲染示例代码的样子:
Copy <body ng-controller="PhoneListCtrl">
<ul>
<li ng-repeat="phone in phones">
{{ phone.name }}
<p>{{ phone.snippet }}</p>
</li>
</ul>
</body>
如果你做的是SPA(Single Page Application),这个问题只会在第一次加载页面的时候出现,幸运的是,可以很容易杜绝这种情形发生: 放弃{{ }}
表达式,改用ng-bind指令
Copy <body ng-controller="PhoneListCtrl">
<ul>
<li ng-repeat="phone in phones">
<span ng-bind="phone.name"></span>
<p ng-bind="phone.snippet">Optional: visually pleasing placeholder</p>
</li>
</ul>
</body>
你需要一个tag来包含这个指令,所以我添加了一个 <span> 给phone name.
Copy angular.module('jqdependency', [])
.directive('failswithoutjquery', function() {
return {
restrict : 'A',
link : function(scope, element, attrs) {
element.hide(4000)
}
}
});
但是这个元素jqLite还是jQuery元素呢?取决于,手册上这么写的:
Angular 中所有的元素引用都会被 jQuery 或者 jqLite 包装;他们永远不是纯 DOM 引用
压缩
特别需要注意的是Angular应用压缩问题。否则错误信息比如 ‘Unknown provider:aProvider <- a’ 会让你摸不到头脑。跟其他很多东西一样,这个错误在官方文档里也是无从查起的。简而言之,Angular依赖参数名来进行依赖注入。压缩器压根意识不到这个这跟Angular里普通的参数名有啥不同,尽可能的把脚本变短是他们职责。咋办?用“友好压缩法”来进行方法注入。看这里:
Copy module.service('myservice', function($http, $q) {
// This breaks when minified
});
to this:
module.service('myservice', [ '$http', '$q', function($http, $q) {
// Using the array syntax to declare dependencies works with minification<b>!</b>
}]);
最终一点建议:如果你想用数组语法复写你的functions,在所有Angular依赖注入的地方应用之。包括directives,还有directive里的controllers。别忘了逗号(经验之谈)
Copy // the directive itself needs array injection syntax:
module.directive('directive-with-controller', ['myservice', function(myservice) {
return {
controller: ['$timeout', function($timeout) {
// but this controller needs array injection syntax, too!
}],
link : function(scope, element, attrs, ctrl) {
}
}
}]);
_注意:_link function 不需要数组语法,因为他并没有真正的注入。这是被 Angular 直接调用的函数。Directive 级别的依赖注入在 link function 里也是使用的。
Directive永远不会‘完成’
在directive中,一个令人掉头发的事就是directive已经‘完成’但你永远不会知道。当把jQuery插件整合到directive里时,这个通知尤为重要。假设你想用ng-repeat把动态数据以jQuery datatable的形式显示出来。当所有的数据在页面中加载完成后,你只需要调用$(‘.mytable).dataTable()就可以了。 但是,臣妾做不到啊!
为啥呢?Angular的数据绑定是通过持续的digest循环实现的。基于此,Angular框架里根本没有一个时间是‘休息’的。 一个解决方法就是将jQuery dataTable的调用放在当前digest循环外,用timeout方法就可以做到。
Copy angular.module('table',[]).directive('mytable', ['$timeout', function($timeout) {
return {
restrict : 'E',
template: '<table class="mytable">' +
'<thead><tr><th>counting</th></tr></thead>' +
'<tr ng-repeat="data in datas"><td></td></tr>' +
'</table>',
link : function(scope, element, attrs, ctrl) {
scope.datas = ["one", "two", "three"]
// Doesn't work, shows an empty table:
// $('.mytable', element).dataTable()
// But this does:
$timeout(function() {
$('.mytable', element).dataTable();
}, 0)
}
}
}]);
在我们的代码里甚至遇到过需要双重嵌套$timeout。还有更疯狂的就是添加<script>tag 到模板中,这个脚本里回调Angular的scope.$apply()方法。我只想说,这很不完美。基于Angular的实现机理,这很难改变。
尽管说了这么多,Angular仍然是我最爱客户端JS框架。你用Angular的时候遇到过其他的坑吗?你用什么方法解决这些问题的呢?请留言!