Ember-ベストプラクティス:工場内の漏れ状態を回避する

DockYardでは、Webアプリケーションの構築からアドオンの作成と保守、Emberエコシステムへの貢献まで、Emberに多くの時間を費やしています。 Emberのベストプラクティス、パターン、アンチパターン、一般的な間違いに焦点を当てた一連の投稿を通じて得た経験の一部を共有したいと考えています。 これはこのシリーズの最初の投稿なので、 Ember.Objectの基本に戻ってEmber.Objectましょう。


Ember.Objectは、Ember開発者として最初に学ぶものの1つであり、 Ember.Objectです。 Emberで作業するほぼすべてのオブジェクトは、ルート(Route)、コンポーネント(Component)、モデル(Model)、またはサービス(Service)であるかどうかにかかわらず、Ember.Objectから継承されます 。 しかし、時々、私はそれが間違って使用される方法を見ます:


 export default Ember.Component.extend({ items: [], actions: { addItem(item) { this.get('items').pushObject(item); } } }); 

これに遭遇したことがある人にとっては、問題は明らかです。


Ember.Object


APIを見て、すべてのInheritedProtected 、およびPrivateオプションの選択を解除すると、 Ember.Objectは独自のメソッドとプロパティがないことがわかります。 ソースコードを短くすることはできません。 これはObservableと混合されたEmber CoreObjectリテラル拡張です:


 var EmberObject = CoreObject.extend(Observable); 

CoreObjectは、ファクトリまたはクラスを定義するためのクリーンなインターフェイスを提供します。 これは基本的に、通常コンストラクタ関数を作成し、メソッドとプロパティをプロトタイプで定義してから、 new SomeConstructor()呼び出してnew SomeConstructor()オブジェクトを作成する方法に関する抽象化です。 this._super()を使用してスーパークラスメソッドを呼び出したり、 不純物を介して一連のプロパティをクラスに結合したりするには、 CoreObject感謝する必要があります。 inmber、 createextend 、またはreopenなど、Emberオブジェクトで頻繁に使用する必要のあるすべてのメソッドがそこで定義されています。


Observableは混合(Mixin)です。これにより、オブジェクトのプロパティの変化、およびgetおよびsetの呼び出し時の変化を観察できます。


Emberアプリケーションを開発する場合、 CoreObjectを使用する必要はありません。 代わりに、 Ember.Objectを継承しEmber.Object 。 結局、Emberには変更に対する最も重要な応答があるため、プロパティ値の変更を検出するにはObservableメソッドが必要です。


新しいクラスのお知らせ


Ember.Objectを拡張することにより、新しいタイプのオブザーバブルオブジェクトを定義できます。


 const Post = Ember.Object.extend({ title: 'Untitled', author: 'Anonymous', header: computed('title', 'author', function() { const title = this.get('title'); const author = this.get('author'); return `"${title}" by ${author}`; }) }); 

Post.create()呼び出すことで、 Post型の新しいオブジェクトを作成できるようになりました。 各レコードについて、 Postクラスで宣言されたプロパティとメソッドが継承されます。


 const post = Post.create(); post.get('title'); // => 'Untitled' post.get('author'); // => 'Anonymous' post.get('header'); // => 'Untitled by Anonymous' post instanceof Post; // => true 

プロパティの値を変更し、投稿に作成者の名前と名前を付けることができます。 これらの値はクラスではなくインスタンスで設定されるため、作成される投稿には影響しません。


 post.set('title', 'Heads? Or Tails?'); post.set('author', 'R & R Lutece'); post.get('header'); // => '"Heads? Or Tails?" by R & R Lutece' const anotherPost = Post.create(); anotherPost.get('title'); // => 'Untitled' anotherPost.get('author'); // => 'Anonymous' anotherPost.get('header'); // => 'Untitled by Anonymous' 

この方法でプロパティを更新しても他のインスタンスには影響しないため、この例で実行されるすべての操作は安全であると考えるのは簡単です。 しかし、これについてもう少し詳しく見ていきましょう。


クラス内の状態リーク


投稿にはタグの追加リストを含めることができるため、 tagsと呼ばれるプロパティを作成できます。デフォルトでは空の配列です。 addTag()メソッドを呼び出すと、新しいタグを追加できます。


 const Post = Ember.Object.extend({ tags: [], addTag(tag) { this.get('tags').pushObject(tag); } }); const post = Post.create(); post.get('tags'); // => [] post.addTag('constants'); post.addTag('variables'); post.get('tags'); // => ['constants', 'variables'] 

動作しているようです! しかし、2番目の投稿を作成した後に何が起こるかを確認しましょう。


 const anotherPost = Post.create(); anotherPost.get('tags'); // => ['constants', 'variables'] 

目標が空のタグを持つ新しい投稿を作成することであった場合(デフォルトで想定)、投稿は以前の投稿のタグで作成されました。 tagsプロパティの新しい値が設定されていないため、メイン配列が変更されただけです。 そのため、状態を事実上Postクラスにスローし、すべてのインスタンスで使用します。


 post.get('tags'); // => ['constants', 'variables'] anotherPost.get('tags'); // => ['constants', 'variables'] anotherPost.addTag('infinity'); // => ['constants', 'variables', 'infinity'] post.get('tags'); // => ['constants', 'variables', 'infinity'] 

これは、インスタンスの状態とクラスの状態を混同できる唯一のシナリオではありませんが、もちろん、より一般的なシナリオです。 次の例では、 new Date()を渡すことで、現在の日時のcreatedDateのデフォルト値を設定できます。 ただし、クラスが定義されると、 new Date() 1回評価されます。 したがって、このクラスの新しいインスタンスをいつ作成するかに関係なく、それらはすべて同じcreatedDate値をcreatedDateます。


 const Post = Ember.Object.extend({ createdDate: new Date() }); const postA = Post.create(); postA.get('createdDate'); // => Fri Sep 18 2015 13:47:02 GMT-0400 (EDT) // Sometime in the future... const postB = Post.create(); postB.get('createdDate'); // => Fri Sep 18 2015 13:47:02 GMT-0400 (EDT) 

状況を管理する方法は?


投稿間でタグを共有しないようにするには、オブジェクトの初期化中にtagsプロパティを設定する必要があります。


 const Post = Ember.Object.extend({ init() { this._super(...arguments); this.tags = []; } }); 

initPost.create()呼び出されるたびにPost.create()れるため、各インスタンスポストは常に独自のtags配列を取得しtags 。 さらに、 tags計算プロパティにすることができます。


 const Post = Ember.Object.extend({ tags: computed({ return []; }) }); 

おわりに


この投稿の冒頭の例のように、このようなコンポーネントを記述してはならない理由は明らかです。 ルートを終了するときにコンポーネントがページに1回だけ表示されても、ファクトリではなくコンポーネントインスタンスのみが破棄されます。 そのため、戻ったとき、コンポーネントの新しいインスタンスには前のページ訪問のトレースがあります。


このエラーは、不純物の使用時に発生する可能性があります。 Ember.MixinEmber.Mixinはないという事実にもかかわらず、その中で宣言されているプロパティとメソッドは、 Ember.Objectと混ざり合っていEmber.Object 。 結果は同じになります。最終的に、混合物を使用するすべてのオブジェクト間で状態を分割できます。



Source: https://habr.com/ru/post/J318096/


All Articles