KO 3.2版除了美妙的HTML自訂元素,還有一項小革新 - pureComputed。

依據官方文件,pureComputed 的 pure 借用自 Pure Function,其主要設計理念在於:

  1. 計算 computed observable 時不應產生任何副作用。
  2. computed observable 結果不因估算次數或其他「隱藏」資訊而不同,只與其他 observable 值相關,對應到 Pure Function,其他 observable 的值相當於輸入參數。

這定義聽起來好學術,但用起來沒這麼複雜。簡單來說,pureComputed 有兩種狀態,Sleeping 與 Listening,在未被訂閱時(訂閱來源可能是其他 computed、pureComputed 或是 data-bind="" 宣告…等等),pureComputed 將停止重算,即便其所依賴的 obervable 改變,也不會重新計算結果。但是當它再被其他來源訂閱追蹤時,其表現就跟一般 computed 完全相同。

pureComputed 最大的好處在於能節省非必要的反覆計算,例如在大型 SPA(Single Page Applcation)中,在某些 UI 不顯示時,其相關 ViewModel 的  computed 並不需持續更新,此時便是使用 preComputed 的好時機。

用一個例子來展示:

在以上程式中,有一個每秒減少1的倒數值 observable(靠 setInterval 驅動),Countdown(n) 及 Computed(p) 分別用 data-bind="text: …" 繫結到 computed 及 pureComputed 寫成的函式取得文字。每次 computed 及 pureComputed 重算時,會在下方 textarea 寫 Log 方便觀察執行次數。最上方的按鈕可以切換 data-bind 繫結的元素出現與否(這裡用 if 切換而非 visible,因為 visible 隱藏時元素仍存在DOM之中,會繼續訂閱 preComputed)。

由實驗結果可以發現,當元素被隱藏,computed 仍會持續重算,但 preComputed 因無人訂閱進入 Sleeping 狀態,不再因 setInterval 改變倒數值重算;當元素恢復顯示,訂閱生效,preComputed  進入 Listening 狀態,也恢復定期重算。

完整程式碼如下:Live Demo

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>KO PureComputed</title>
  <style>
  </style>
</head>
<body>
  <input type="button" value="Toggle Panel" data-bind="click: toggle"/>
  <div data-bind="if: showPanel">
    <div data-bind="text: countdownDispN"></div>
    <div data-bind="text: countdownDispP"></div>
  </div>
  <textarea id="logger" style="width: 480px; height: 200px;">
  </textarea>
  <script src="http://knockoutjs.com/downloads/knockout-3.2.0.js"></script>
  <script>
    var logger = document.getElementById("logger");
    function log(msg) {
      logger.value += ", " + msg;
    }
    function myViewModel() {
      var self = this;
      self.countdown = ko.observable(256);
      self.showPanel = ko.observable(true);
      self.toggle = function() {
        self.showPanel(!self.showPanel()); 
      };
      self.countdownDispN = ko.computed(function() {
        log("computed");
        return "Countdown(n)=" + self.countdown();
      }); 
      self.countdownDispP = ko.pureComputed(function() {
        log("pureComputed")
        return "Countdown(p)=" + self.countdown();
      });
    }
    var vm = new myViewModel();
    setInterval(function() {
      vm.countdown(vm.countdown() - 1);
    }, 1000);
    ko.applyBindings(vm);
  </script>
</body>
</html>

最後補充一點:pureCompouted 在每次由 Sleeping 切換到 Listening 時也會重算,而 computed 只有在相依 observable 變化時才重算,若遇到頻繁切換 Sleeping / Listening 狀態的情境,使用 pureComputed 的效能反而不如 computed。

[KO系列]

http://www.darkthread.net/kolab/labs/default.aspx?m=post

Comments

# by LaLaLiLaLa

不知道黑暗大有沒有用過knockoutmvc? http://knockoutmvc.com/ 個人認為最大的好處應該是比較有結構, intelliSense和執行時期debug較方便(ASP.Net MVC)

# by Jeffrey

to LaLaLiLaLa, 有試過,但近年來工作漸以SPA專案為主,多靠HTML+JS運作,很少寫cshtml,故使用經驗不多。

Post a comment