寫過 AngularJS 的人應該都知道,網路上批評 AngularJS 最大的問題就是
“AngularJS 效能很差”
效能很差的最大原因是因為 AngularJS 要實作如魔法般的 data binding 。
簡單來說,就是會幫你檢查所有你希望被檢查的每一個 model (data binding)。
如果有改變,就幫你重新 render element DOM。($digest cycle)
其實不是每一個值我們都想要讓 AngularJS 檢查,因為我們知道有些值並不會被外在改變。(static value)
這時候,強烈建議把 value 寫死就好,不需要每筆資料都要跟後端要或是由 AngularJS 綁定。
ex: 外部網站聯結 (不需要用 ng-href )
<a href=“http://google.com”></a>
ex: sidebar 選單 (不需要用 ng-repeat)
<ul>
<!— 選單不會被改變,寫死 -->
<li>選單一</li>
<li>選單二</li>
<li>選單三</li>
</ul>
可是假如當畫面上的顯示是動態產生的話,
因為不知道資料樣子,所以寫死就是不可能的。
比方說,畫面列表需要顯示後端傳來資料。
顯示從後端撈取的海賊王資料
範例:http://plnkr.co/edit/ntp94iMbhpXSBYX2DgoP
<table>
<tbody>
<!— Ctrl.list 從後端來的 —>
<tr ng-repeat="item in Ctrl.list”>
<!-- 在 controller 中撈取 title by id -->
<td>{{Ctrl.getTitleFromId(item.id)}}</td>
<td>{{item.price | number}}</td>
</tr>
</tbody>
</table>
我們在 getTitleFromId function 插入檢查點,讓每次進入時會增加 count 一次。
結果發現在一開始初始化的時候,getTitleFromId function 就已先跑過 90 次。
(列表有 9 個人 * 10 (magic number))
額外在上面增加一個跟列表完全沒關係的 input。
每次修改 input 時,會發現 count 都會在往上增加。
為了效能好,當然希望不會改變的值,不會被 $digest cycle 檢查,造成效能問題。
這時候 bindonce 就是登場的時候了~登登登
先載入 bindonce ,在想要只偵測一次的地方放入 bindonce directive 的名稱。
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-bindonce/0.3.1/bindonce.min.js"></script>
angular.module('myExample', ['pasvaz.bindonce']); // 引入 bindonce module
<table>
<tbody>
<tr ng-repeat="item in Ctrl.list" bindonce="item">
<td bo-text="Ctrl.getTitleFromId(item.id)"></td>
<td bo-text="item.price | number"></td>
</tr>
</tbody>
</table>
上面的範例,在使用 bindonce 後,count 在初始化後下降到只有 9 次。
並且後來修改 input 的值都不會讓 getTitleFromId 在重新跑,count 也不會再增加了。
範例:http://plnkr.co/edit/5fZoEVCqzIDEHTZcWjFp?p=preview
bindonce 原理:
AngularJS 會在每個 ng-* 中產生一個 watcher 來確保 data 保持更新。
而 bindonce 就是不使用 AngularJS 的方式產生 data (bo-*)。
讓指定的 data ,再不產生 watcher 的情況下顯示文字。
該 data 因沒有 watcher 而不會跑檢查 ($digest cycle),效能就會提升。
bindonce 提供的簡易原理 directive :
<span my-custom-set-text="Person.lastname"></span>
...
<script>
angular.module('testApp', [])
.directive('myCustomSetText', function () {
return {
link:function (scope, elem, attr, ctrl) {
// 非 data binding 的方式產生 text value
elem.text(scope.$eval(attr.myCustomSetText));
}
}
});
</script>
用以上方法可以讓 AngularJS 再不產生 watch 的情況下,把 text 的值顯示出來。
看官網又更詳細的說明喔。