LoginSignup
42
45

More than 5 years have passed since last update.

knockout.jsでそこそこ多めのデータセットを扱う際の注意点

Last updated at Posted at 2013-12-05

はじめに

クライアントサイドMVVMフレームワークであるknockout.jsに関する話題。
数百〜数千件のデータを扱うときの注意点・ハマりどころについて。

ObservableArray

配列の監視を行うObservableArrayを用いる場合に、こんな処理を書くとパフォーマンスは著しく低下する。数百件のデータを扱うのに10秒近くかかる。

var ViewModel = function() {
  this dataset = ko.observableArray([]);

  this addDataset = function(items) {
      var i;
      var length = items.length;
      for (i = 0; i < length; i++) {
          dataset.push(new Result(items[i])); // ここがボトルネック
      }
  };
};

ここでのdataset.push()はknockoutが提供するメソッドで、jsの標準関数と同等の働きをしつつ、リスナに対して変更を通知する(ちなみにdatasetはあくまで関数である)。この書き方では1件1件pushされる度に通知が飛ぶために、パフォーマンスは著しく下がる。オブジェクトを生成して1件1件配列に突っ込んでいるのだからもちろん遅いのだが、ドキュメントをさらっと読んだだけだとうっかりハマってしまうので注意。

knockoutの挙動で「?」となったら、ソースコードを見てみると案外すぐ分かったりする。
(今回はこの辺

これを改善するためには、datasetの変更を1度で済ませる必要がある。通知自体は、observable.valueHasMutated()関数によって行われるので、このような書き方が考えられる。参照を持つ配列に一度渡してから最後に通知する方式だ。

var ViewModel = function() {
  var self = this;

  this dataset = ko.observableArray([]);

  this addDataset = function(items) {
      var underlyingArray = self.dataset();
      var i;
      var length = items.length;
      for (i = 0; i < length; i++) {
          underlyingArray.push(new Result(items[i]));
      }
      self.dataset.valueHasMutated();
  };
};

kncokout.utilsとapply()を用いる

もっとシンプルな方法は、knockout.utils内にある便利な関数を使うことだ。

var ViewModel = function() {
  var self = this;
  this dataset = ko.observableArray([]);

  this addDataset = function(items) {
    var newItems = ko.utils.arrayMap(items, function(item) {
      return new Result(item);
    };
    self.dataset.push.apply(self.dataset, newItems);
  };
};

大量のデータを配列に追加してViewに通知するときには、通知回数について意識すべきである。このような場面にはよく遭遇するので、覚えておきたい。

42
45
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
42
45