パート 1: Immutable.js を使用した AngularJS アプリケーションの改善
Rescale では、Facebook の世界に挑戦してきました。 オープンソースライブラリ Immutable.js を使用します。 Immutable.js は、ECMAScript 5 では使用できない Set、Map、List、Stack などの JavaScript の不変コレクションを提供するライブラリです。これらのデータ構造により、コードの可読性が向上し、場合によってはパフォーマンスが向上することがわかりました (続きを読む)この例を参照してください)。
Immutables を使用することに決めたもう XNUMX つの理由は、UI の「状態」を変更するさまざまな方法によって発生する問題を解決することでした。 Immutable.js を使用すると、Plain Old JavaScript Objects (POJO) を不変マップに置き換えることができ、モデル内で何かが変更されるたびに、ビューには常に最新のステータス オブジェクトへの正しい参照が含まれるようになります。
要約すると、Immutable.js を使用する利点は XNUMX つの点に集約されます。
- マップ、セット、リスト、スタックなどのデータ構造へのアクセス
- 複数の状態に関連する問題を排除する不変の設計パターン
この記事では、最初の利点を説明するために、OrderedSet を使用してコードを簡素化する方法を示します。 まず元のコードを紹介します。コードを読む前に、例のコンテキストをいくつか説明します。
を追跡する UI のモデルを構築したいと考えています。 選択されたファイル 複数のページにわたって、ユーザーの要求に応じてサーバーから取得されます。 合計で数千のファイルが存在する可能性があるため、UI は一度に XNUMX ページのみを保持しますが、ユーザーが選択したすべてのファイルを任意の数のページにわたって保存します。 この例では、現在表示されているファイルを選択/選択解除する「すべて選択/すべて選択解除」ボタンを具体的に実装します。
また、項目は他のページからすでに選択されている可能性があるため、displayFiles 内のすべてを selectedFiles に単純に追加することはできません。これは、selectedFiles 内に重複コピーが作成される可能性があるためです。 したがって、参照ではなく ID を使用して File オブジェクトを比較する必要があります。 すべての要件を満たすには、最終的に次のようなものを書くことになります。
function FileManager() { this.displayFiles = []; this.selectedFiles = []; } // ユーザーが「すべて選択 / すべて選択解除」ボタンをクリックしたときにトリガーされるメソッド FileManager.prototype.toggleDisplayFiles = function(toggleValue) { var self = this; var containsInArray = function(arr, obj) { return _.some(arr, function(o) { return obj.id === o.id; }); }; // toggelValue が true -> ユーザーは表示されているすべてのファイルを選択したい if (toggleValue) { _.each(self.displayFiles, function(df) { if (existsInArray(self.selectedFiles, df)) { self.displayFiles .push(df); } }); } else { for (var i = self.selectedFiles.length - 1; i >= 0; i--) { var sf = self.selectedFiles[i]; if (existsInArray(self.displayFiles, sf)) { self.selectedFiles.splice(i, 1); } } } }
を多用しているため、明確ではない場合があります。 強調する ヘルパー メソッドですが、本質的には、selectedFiles 配列内の項目の追加や削除、または削除プロセスにネストされた for ループを使用しています。反復に影響を与えずに配列を結合できるように、selectedFiles 配列を最後から反復処理しています。 確かに、より多くのヘルパー関数とより優れた簿記を使用して最適化することはできますが、すでにセットとして知られているものを実装するための車輪の再発明をしていると感じずにはいられません。
OrderedSet を使用すると、コードは次のように簡略化できます。
function FileManager() { this.displayFiles = Immutable.OrderedSet(); this.selectedFiles = Immutable.OrderedSet(); FileManager.prototype.toggleDisplayFiles = function(toggleValue) { if (toggleValue) { this.selectedFiles = this.selectedFiles.union(this.displayFiles); } } else { this.selectedFiles = this.selectedFiles.subtract(this.displayFiles); } };
確かに短いですが、このアプローチにはまだ完全には公開されていない魔法があります。 前のコードでは、ファイルを比較するための重要な要件の XNUMX つは次のとおりでした。 参照ではなく ID を比較する。 OrderedSets でこの動作をサポートするには、 ハッシュコード メソッドの フィレット クラスの一意性を定義し、 等しい Java で、equals および hashCode をオーバーライドするクラスで行うのと同様の方法でメソッドを作成します。 この例では次のようになります。
File.prototype.hashCode = function() { var hash = 0, i, chr, len; if (id.length === 0) ハッシュを返します。 for (i = 0, len = this.id.length; i < len; i++) { chr = this.id.charCodeAt(i); ハッシュ = ((ハッシュ << 5) - ハッシュ) + chr; ハッシュ |= 0; // 32 ビット整数に変換 } ハッシュを返します。 }; File.prototype.equals = function(other) { return this.id === other.id; };
クラスには追加のコードがいくつか追加されていますが、元のメソッドははるかに短く、コードの読みやすさは大幅に向上しています。 他にも実装すべき方法がたくさんあることを考慮すると、 フィレット オブジェクト、追加 ハッシュコード や 等しい メソッドの フィレット クラスは彼らの言う価値があるでしょう。
最後に、selectedFiles の型を通常の配列ではなく OrderedSet のインスタンスに変更したため、コントローラーに次の watch ステートメントを追加する必要があります。
var fm = 新しい FileManager(); $scope.$watch(function() { return fm.selectedFiles; }, function(newSF) { $scope.selectedFiles = newSF.toArray(); });
ここで、レンダリングにパフォーマンスの問題があるのではないかと疑問に思われるかもしれません。これは、selectedFiles を更新するたびに、XNUMX つの項目を追加する場合でも、数百の項目を削除する場合でも、$scope.selectedFiles の参照を新しい配列に更新するためです。 ただし、File オブジェクト自体は Angular の $$hashKey プロパティを含む同じオブジェクトであるため、Angular は $$hashKey を使用して新しい配列内の項目を比較し、必要最小限の DOM 更新を行います。 実際、あまり頻繁に変更されない巨大なデータ構造に対して多数のウォッチャーがある場合、より単純な $watch ステートメントによりパフォーマンスがいくらか向上します。 ただし、モデル内で何かが変更されるたびに新しいデータ構造を作成すると、ある程度のオーバーヘッドが発生することを覚えておくことも重要です*。
フロントエンド開発では、ECMAScript 5 では利用できない Java Collections API にあるデータ構造 (Map、Set、Stack、Queue など) が必要な場合があります。そのような状況では、Immutable.js を使用すると役立ちます。コードの読みやすさだけでなく、アプリケーションのパフォーマンスも向上します。 いつものことですが、これはすべての場合に効果がある万能薬ではありませんが、多くの場合、有益であることがわかります。
*単純なウォッチステートメントで得られる利点と、新しいデータ構造の作成によって発生するコストとの間のトレードオフについて詳しく知りたい場合は、これを確認してください。 記事.