私は卒業証書を書いており、そこで問題の1つ、匿名の高速Webチャットの実装を解決しています。 あらゆる意味で高速-ダウンロード、アプリケーションの操作、使用(許可なし)。 クライアント側のNode.jsフレームワークSocketStreamとAngularJSで、選択はひとまず止まりました。 その過程で、同じモデルのフィルターによって計算が繰り返されるという問題に遭遇しました。 問題と解決策の詳細。
読者レベル:
AngularJS: Medium(フィルターの作成)
Lo-Dash: 「のこぎり」
ソリューションに直行します
問題は詳細にあります
要素を操作することでアプリケーションが常に機能する大きな配列があります。 配列フィルターを配列に適用する必要があります。たとえば、日付で並べ替えたり、特定のプロパティを持つ要素を強調表示したりします。 この問題をアプリケーション領域に転送します-私のチャットの簡易版です。 配列の要素は、メッセージを含むチャット(ルーム/サークル)です。 チャットの構造は次のとおりです。
{ id: 'rE4aA', title: ' ', online: 3, recent: 0, // messages: [] // }
ngRepeat
{N}ディレクティブを使用して(画面サイズに応じて)ページにチャットの数を表示したい。 また、チャットのタイトルでマウスの右ボタンをクリックすると表示されるコンテキストメニューを表示し、選択したチャットを別のチャットの場所に移動できるようにします。 これはどのように見えるかです:
チャットのタイトルを右クリック
チャットのハイライト、その場所に移動をマークします
このような機能を実装するには、
ngRepeat
ディレクティブを使用して2つのリストを作成し、フィルターを適用します。 チャットの場合、フィルターは新しいメッセージの数(最近のプロパティ)で並べ替え、要素(チャット)の数をブラウザーウィンドウのサイズから計算される数{N}に減らすことができます。 コンテキストメニューの場合-現在の要素を除く同じフィルター(クリックされたヘッダーのチャット)。
フィルターコード:
angular.module('app') .filter('opened', ['$rootScope', function($s){ return function(o){ console.log(' «opened»'); var count = $s.count; // , {N} return _(o) // Lo-Dash .sortBy('recent') // .reverse() // ( ) .first(count) // {N} .value() // } }]);
このフィルターを各
ngRepeat
ディレクティブに渡される配列引数に適用すると、コンソールに「フィルターを適用しました」というメッセージが2回表示される
ngRepeat
わかります。 これは、リソースの半分がフィルターによって浪費されたことを意味します。 コンテキストメニューなどの利便性により、アプリケーションの現在の状態のレンダリング時間が2倍になりました。 また、同じデータとフィルターを使用して機能を追加し続けると、状況はさらに悪化します。
問題解決
解決策は、フィルターされた配列を返す関数を作成することです。 この関数は、ネイティブフィルタープロバイダーを使用せずに、元の配列の代わりに使用されます。 この関数は、キャッシュ機能を実装するLo-Dashプロパティmemoizeでラップします。 以下に、memoizeの仕組みと実装例を示します。
Lo-Dashプロパティメモ
引数:
-
-
(必須) -この関数のキャッシュされた結果は、memoizeを提供します -
-
(オプション) --
の結果はキャッシュキーです(一意性を確認します)
_.memoize(fn、[fn])は関数を返し、最初に呼び出されたときに、計算し、結果を記憶して(キャッシュを作成して)返します。 後続の呼び出しはキャッシュを返します。 これはすべて、単一のキャッシュキーに当てはまります。
キャッシュキーは、2番目の引数として渡される関数の結果によって決定されます。 デフォルトでは(2番目の引数が指定されていない限り)、memoizeは最初の引数をキャッシュキーとして使用します。
鮮やかな例
短いリストの最後にデモへのリンクがありますが、コード内のコメントに注意することをお勧めします。
1つの接着された「フォーム」オブジェクトを使用して単純なコントローラーを作成します。
function MyController($scope){ $scope.form = { input: {key:'', val:''}, // array: [ {key:'pear', val:''}, // {key:'melon', val:''}, {key:'ananas', val:''}, {key:'cherry', val:''} ], order: 'key', // key (2 key/val) check: false, // . (2 — true/false) add: function(){ // this.array.push(angular.copy(this.input)); this.filtered.cache = {} // }, filtered: _.memoize( // function(){ console.log(' : ' + this.order + ' ' + this.check); return _.sortBy(this.array, this.order) }, function(){ // - // return [this.order, this.check] } ) } }
少しのHTML:
<form name="myform" ng-app ng-controller="MyController"> <input type="text" required ng-model="form.input.key" placeholder="key"> <input type="text" required ng-model="form.input.val" placeholder="val"> <button ng-disabled="!myform.$valid" ng-click="form.add()"></button><br><br> <fieldset> <legend> : <select ng-model="form.order" ng-options="p for p in ['key', 'val']"></select> </legend> <div ng-repeat="el in form.filtered()"> {{el.key}} — "{{el.val}}" </div><br> <label> <input type="checkbox" ng-model="form.check"> - </label><hr> <pre>{{form.filtered()|json}}</pre> </fieldset> </form>
jsFiddleの結果を見てみましょう。
Ctrl
+
Shift
+
J
コンソールを開き
Shift
(Chromeブラウザーに関連)。 ソートを切り替えてフラグをプルしようとします。 コンソールでは、(各状態に対して)最大4つのフィルター機能の開始が表示されます。 配列に新しい要素を追加する-キャッシュをリセットし、このソリューションが正しく機能することを再度確認します。
素晴らしいLo-Dashライブラリのおかげで、具体的にはmemoizeプロパティのおかげで、AngularJSアプリケーションの速度を大幅に向上させることができました。 ネイティブフィルターを適用した場合、アプリケーションが起動された瞬間から、フィルターは1に対して8回機能しました(memoizeを使用したソリューション)。
コミュニティからの建設的な批判と、ネイティブフィルターを「ポンピング」する方法についての考えを楽しみにしています。
PS: Habrに招待してくれたUFO に感謝します。