iT邦幫忙

DAY 28
0

我在前端 ng 時系列 第 28

可以再快一點嗎? - 介紹 bindonce

寫過 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 的值顯示出來。
看官網又更詳細的說明喔。


上一篇
AngularJS with 觸控裝置
下一篇
生生不息的 watcher - 介紹 data-binding
系列文
我在前端 ng 時30

尚未有邦友留言

立即登入留言