(アーカイブ)Matreshka.js 1.1:さらにクール

画像



ロシア語のドキュメント

Githubリポジトリ



みなさんこんにちは。 今日、9月28日は、Matryoshkaリポジトリへの最初のコミットから2年を迎えます。 そのため、この時点で、JavaScript開発者(マトリョーシカをフレームワークとして使用したくない人にとっても)のあらゆる種類の特典を備えた新しいリリースが到着しました。



マトリョーシカは、アクセサーに基づくJavaScriptフレームワーク(または必要に応じてライブラリー)であり、それらから一目で信じられないほどの可能性を絞り出します。 ゲッターとセッターがJavaScriptに登場したときのことを覚えていますか? 周囲の騒音はどれくらいでしたか...記事、会話...そして、すべてが落ち着きました。多くの人は、簡単な例を除いて、これらの機会の使い方を理解していませんでした。 マトリョーシカは、JavaScriptでアクセサーが必要な理由の質問に対する優れた回答です。







伝統的に、このフレームワークが小さなコードの助けを借りて何ができるかを思い出させてください。



以前は、これしかできませんでした。



// this -   //   "x"     this.bindNode('x', 'input.my-node'); //  ,  alert this.on('change:x', function() { alert(this.x); }); //  ,   //     "x"  this.x = 'Wow!';
      
      





これを行うこともできます:



 var object = {}; //   "x"     MK.bindNode(object, 'x', 'input.my-node'); //  ,  alert MK.on(object, 'change:x', function() { alert(object.x); }); //  ,   //     "x"  object.x = 'Wow!';
      
      





ChromeとNodeJSの最新バージョンが最終的にES6構文のほとんどの要素をサポートし始めたという事実により、この投稿の以下の例はすべてES6で記述されます。 このような簡単な方法で、これらの革新を信じられないほどクールだと考えているすべての人を祝福し、それらに精通していない人のためにES.nextに注目を集めたいと思います。



ネイティブオブジェクトのサポート



最も重要な革新は、マトリョーシカによる任意のオブジェクトのサポートでした。 はい、はい、バインディングを宣言したり、他のクールなことをしたりするために、マトリョーシカのインスタンスを作成する必要はありません。



 let object = {}; //  "x”   '.my-node' MK.bindNode(object, 'x', '.my-node'); // "y”      x  z MK.linkProps(object, 'y', 'x z', (x, z) => x + z); //”z” -   ,   ,      MK.mediate(object, 'z', Number); // ...
      
      





この例からわかるように、新しい静的メソッドは動的メソッドを完全に繰り返しますが、わずかな違いがあります。元のオブジェクトを最初の引数として渡す必要があります。



 // this.bindNode('x', '.my-node'); // MK.bindNode(object, 'x', '.my-node');
      
      





さらに、それ自体をレンダリングできるコレクションでは、 Matreshkaクラスから継承されていたモデルを指定する必要がなくなりました。



 class MyArray extends MK.Array { itemRenderer() { return '<li>'; } constructor() { super().bindNode('sandbox', '.some-node'); } } let arr = new MyArr(); arr.push(someData);
      
      





新しい静的メソッドの完全なリストは、 MatreshkaMagicセクションにリストされてます。



マトレシュカマジックライブラリー



ネイティブオブジェクトのサポートのおかげで、すべての「マジック」関数を、 MatreshkaMatreshka.ArrayMatreshka.ObjectクラスおよびClass関数を含まない、よりコンパクトな別のライブラリに取り込むことが可能になりました。 開発者は、 MatreshkaMagic



オブジェクトまたはMatreshka



クラスのすべての静的メソッドを含む短いバージョンのmagic



アクセスできます。



ライブラリはmagic / repositoryフォルダーにあります。



 magic.bindNode(object, 'x', '.my-node'); magic.linkProps(object, 'y', 'x z', (x, z) => x + z); //  . .
      
      





ライブラリーの詳細については、ドキュメントを参照してください



「ディープバインディング」



ネイティブオブジェクトのサポートにより登場したもう1つのクールな機能は、いわゆる「ディープバインディング」です。 任意のネストのオブジェクトを使用すると、DOMノードをオブジェクトの深部の任意のプロパティに関連付けることができます。



 this.a = {b: {c: {d: 41}}} this.bindNode('abcd', '.my-node');
      
      





Matryoshkaカーネルはオブジェクトのブランチ全体を監視し、ブランチ内のオブジェクトの1つが再定義された場合にバインディングを再インストールします



 this.ab = {c: {d: 42}};
      
      







ディープリンク



マトリョーシカには長い間linkPropsメソッドが含まれていたため、一部のプロパティの依存関係を他のプロパティに設定できます。



独自のプロパティへの依存を設定できます。



 this.linkProps('a', 'bc d', (b, c, d) => b + c + d); this.b = 1; this.c = 2; this.d = 3; alert(this.a); // 6
      
      





他のオブジェクトのプロパティへの依存を設定できます。



 this.linkProps('a', [ externalObject, 'b', externalObject2, 'c', this, 'd' ]); externalObject.b = 1; externalObject2.c = 2; this.d = 3; alert(this.a); // 6
      
      





linkProps



は、プロパティパスの指定をサポートするようになりました。



 this.linkProps('a', 'bcd e.f', (d, f) => d + f); this.b = {c: {d: 40}}; this.e = {f: 2}; alert(this.a); // 42
      
      





プロパティへのパスのチェーンで何かが変更されると、マトリョーシカはこの変更をインターセプトし、古いサブチェーンとの接続を切断し、新しいチェーンへの依存関係を作成します。



 this.bc = {d: 1};
      
      





前と同様に、他のオブジェクトのプロパティへの依存関係を作成できます。また、前述のように、どのオブジェクトもソースオブジェクトとして機能できます。



 let targetObject = {}, o1 = {b: {c: {d: 40}}}, o2 = {e: {f: 2}}; MK.linkProps(targetObject, 'a', [ o1, 'bcd', o2, 'ef' ], (d, f) => d + f); alert(targetObject.a); // 42
      
      







委任されたイベントの透過的な構文



以前のバージョンでは、現在のオブジェクト( this



)だけでなく、任意のネストのオブジェクトでもイベントをハングさせることが可能でしたことを思い出してください。 しかし、構文は貧弱でした。 小さな例を挙げます。 マトリョーシカのインスタンスにはいくつかのプロパティがあるか、あるべきであり、それがマトリョーシカのインスタンスでもあるとします。



 this.a = new Matreshka();
      
      





プロパティを割り当てる前または後に、このプロパティに関連するイベントのハンドラを作成できます。 このために、「犬」を含む構文が使用されました。



 this.on('a@someevent', handler); this.a.trigger('someevent');
      
      





配列オブジェクト (マトリョーシカではキーと値のコレクション)の場合、イベントはコレクションに含まれるすべての要素でリッスンされるため、ターゲットプロパティを指定しないことができました。



 this.on('@someevent', handler); this.push(new Matreshka()); this[0].trigger('someevent');
      
      





一見シンプルに見えます。 しかし、オブジェクトのツリーがもう少し複雑な場合はどうでしょうか? たとえば、プロパティ"a"



にはコレクションが含まれます。



 this.a = new MK.Array(); this.a.push(new Matreshka());
      
      





そのようなコレクション内でイベントをキャッチする方法は? 「オブジェクト内"a"



イベント"a"



キャッチする"@someevent"



->イベントをキャッチする配列の要素内の"@someevent"



という2つの犬を組み合わせることができます。



 this.on('a@@someevent', handler); this.a[0].trigger('someevent');
      
      





これはまだ経験できます(十分なコーヒーを飲む場合)。 しかし、もっと深くしたい場合はどうでしょうか? その後、「犬」の数が増加し、コーヒーはもはや役に立ちません...



同意して、この機能の可能性は非常に大きいです。 入れ子のデータイベントをリッスンできます。たとえば、配列の配列に含まれるオブジェクトのプロパティの変更などを学習できます。したがって、デリゲートされたイベントの構文をわずかに変更することにしました。 「Doggie」は残りましたが、オブジェクトへのパスとイベントの名前の唯一の区切りとして。 イベントがネストされたオブジェクトに触れると、犬はドットに置き換えられます。 顔のない犬ではなく、コレクション内の何かについて知りたい場合は、アスタリスクを使用します。 ここで、いくつか停止する必要があります。

ハンドラーをプロパティ""



にアタッチする場合、構文は同じままです。



 this.on('a@someevent', handler);
      
      





コレクションアイテムでイベントをキャッチする場合は、次の代わりに:



 this.on('@someevent', handler);
      
      





このように書きます:



 this.on('*@someevent', handler);
      
      





アスタリスクは、「 MK.Objectのデータを処理するプロパティ 」または「 MK.Arrayコレクションの任意の要素」を意味します。



もっと深く行きます。 上記の次の例を組み合わせる必要があります。



 this.on('a@@someevent', handler);
      
      





次のように書きます:



 this.on('a.*@someevent', handler);
      
      





構文はずっと簡潔です。 オブジェクトのパスを指定する必要があるのは、@の前と、イベントの名前を指定した後です。



イベントに関する詳細な記事。



setClassFor



setClassForは、もう1つの非常に優れた機能です。 与えられたプロパティがクラスのどのインスタンスであるかを示します。 プロパティを上書きしようとすると、内部インターセプターは割り当てられるのではなく、新しいデータで更新します。 例を見てみましょう。



 //     ( ) this.x = {a: 41}; //     this.setClassFor('x', MyClass); // ,      MyClass console.log(this.x instanceof MyClass); // true //   "a”  console.log(this.xa); // 41 //    //   "x”    var x = this.x; //    this.x = {a: 42}; // ,    console.log(xa); // 42 // ,       console.log(x === this.x); // true // Wow!    ,   !
      
      





オブジェクトの深い構造があり、 setClassFor



もネストされたオブジェクトで実行されている場合、興味深いことができます。 たとえば、階層化されたデータのプレゼンテーションをローカルストレージに保存します。



 localStorage.x = JSON.stringify(this.x);
      
      





そして、魔法の杖の波でそれらを復元します。



 this.x = JSON.parse(localStorage.x);
      
      





または、サーバーに行き来します。



そのようなロジックが必要になる可能性がある信じられないほど多くのケースがあります。 別の例として、ドキュメントのコードを示します(簡潔にするために、ECMAScript 7のクラスプロパティを使用します)。



 // app.js class App extends MK { constructor(appData) { this.appData = appData; this.setClassFor('appData', AppData); } } // app-data.js class AppData extends MK.Object { constructor(data) { super(data) .setClassFor({ friends: Friends, settins: Settings }); } } // friend.js class Friend extends MK.Object { constructor(data) { super(data); } } // friends.js class Friends extends MK.Array { Model = Friend; constructor(data) { super(...data); } } // settings.js class Settings extends MK.Object { constructor(data) { super(data) .setClassFor('credentials', Credentials); } } // credentials.js class Credentials extends MK.Object { constructor(data) { super(data); } } // app-init.js var app = new App({ settings: { name: 'Vasiliy Vasiliev', credentials: { email: 'vasia.vasia@gmail.com' } }, friends: [{ name: 'Yulia Zuyeva', id: 1 }, { name: 'Konstantin Konstantinopolsky', id: 2 }, { name: 'nagibator3000', id: 3 }] }); //        JSON.stringify(app.appData); //       appData //  ,     app.appData = { settings: { name: 'Petr Petrov', credentials: { email: 'petr.petrov@gmail.com' } }, friends: [{ name: 'Yulechka Zuyeva', id: 1 }, { name: 'Konstantin Konstantinopolsky', id: 2 }] };
      
      





メソッドドキュメントの詳細。



DOMテンプレートエンジン



Matryoshkaは、ロジックをHTMLコードで記述することを強制するMVVMパターンを実装するフレームワークとは対照的に、JSファイルにロジックを含めるべきだという考えを公言するフレームワークです。



JSファイルにロジックを実装することは非常に便利です。 ただし、すべてのバインディングの記述がコードの行数の観点からコストがかかりすぎる場合に、状況が発生することがあります。



そのため、DOMテンプレートエンジンを改善および高速化することが決定されました。これは最近まで公式APIにはありませんでした。 彼は何をしていますか? DOMノード、DOMノードのコレクション、HTMLコード、または現在のオブジェクトのサンドボックスを受け取り、それを解析して、 {{KEY}}



ような角のような構造を見つけ、これらの構造が見つかったバインディングを作成します。



 <a href="http://{{website.domain}}/{{category}}/{{page}}">Look at the {{info.title}}</a>
      
      





 this.parseBindings(); this.website.domain = 'example.com'; this.category = 'foo'; this.page = 42; this.info.title = 'cool stuff';
      
      





この方法の詳細については、ドキュメントを参照してください



この方法は、テンプレートにロジック(ループ、条件、ハンドラー)がないため、マトリョーシカのイデオロギーと矛盾しません。



メソッド自体のAPIの公開に加えて、コレクションのテンプレートエンジンがデフォルトで有効になりました( useBindingsParser: true



を記述する必要はなくなりました)。



 class MyArray extends MK.Array { itemRenderer = '<span>Hello, {{name}}</span>'; ... }
      
      







ECMAScript 2015のさらなる砂糖





setClassFor



例は、メソッドがsuper()



直後に起動されることを示しています。 この可能性は、非常に簡単な変更のおかげで実現しました。3つのコンストラクター( Matreshka



Matreshka.Array



Matreshka.Object



)はMatreshka.Array



undefined



ではなくMatreshka.Object



返します。



 class MyObject extends MK.Object { constructor(data) { super(data) .bindNode('x', '.my-node'); } } //   MyObject   a  b, //    myObject = new MyObject({a: 1, b: 2}); class MyCollection extends MK.Array { constructor(data) { super(...data) .bindNode('x', '.my-node'); } } //  ,   5  myCollection = new MyCollection([1,2,3,4,5]);
      
      







ononceonDebounceメソッドでのイベント型オブジェクトのサポート



以前のバージョンでは、開発者はイベントハンドラーを宣言するための唯一の構文を使用できました。



 this.on('eventname1', handler1); this.on('eventname2', handler2);
      
      





これで、適切なメソッドを1回だけ呼び出すことで、複数のハンドラーを宣言できます。



 this.on({ 'eventname1': handler1, 'eventname2': handler2 });
      
      





この記事では、このニュースについては言及していませんが、ECMAScript 2015を使用すると、マイクロタスクのコードを大幅に短縮できます。



 this.on({ 'eventname1': evt => this.x = 42, 'eventname2': evt => doSomethingElse() });
      
      





「古い」構文に対して:



 this.on({ 'eventname1': function(evt) { this.x = 42 }, 'eventname2': function(evt) { doSomethingElse(); } });
      
      







itemRendererのオーバーライド



itemRenderer



は、コレクションの要素を描画する方法について説明するコレクション( Matreshka.Array



)の仮想プロパティです。



 //  ,  ES7 class MyCollection extends MK.Array { itemRenderer = "<div>Hi there!</div>"; constructor() { super() .bindNode('sandbox', '.array-sandbox') .push({a: 1}, {a: 2}); } }
      
      





詳細については、itemRendererのドキュメントを参照してください。



新しいバージョンから、 itemRenderer



オーバーライドすると、コレクションが自動的に再描画されます。



 //   - span this.itemRenderer = '<span>I'm a span</spam>'; //   - div this.itemRenderer = '<div>I'm a div</div>';
      
      





いくつかのユーザーケースを考え出すことができます。1つのボタンでコレクションのデザインを変更するか、テンプレートがサーバーにあります(以下の例では、 Fetch APIが使用されます )。



 fetch('templates/my-template.html') .then(resp => resp.text()) .then(text => this.itemRenderer = text);
      
      





サーバーがテンプレートを提供するまで待機することなく、アイテムの挿入、削除、並べ替えなど、通常どおりコレクションを操作できます。 サーバーから戻ると、コレクション自体がページに描画されます。



新しいバインダー



バインダーは、ページ上の要素にプロパティを関連付ける方法を示すオブジェクトであることを思い出させてください。 バインダーは、1方向または2方向のバインディングを実装するbindNodeメソッドによって使用されます。



 this.bindNode('x', '.my-node', binder); this.x = 42; //     
      
      





詳細な説明は、メソッドのドキュメントに記載されています



MK.binders.progress-プロパティをプログレス要素のHTML5状態に関連付けます。 バインダーは標準バインダーのコレクションの一部であるため、手動で呼び出す必要はありません。



 this.bindNode('x', '.my-progress'); this.x = 42; //     42
      
      





MK.binders.innerText-プロパティをtextContent



またはinnerText



プロパティを持つ要素のテキスト値にtextContent



ます。



 this.bindNode('x', '.my-node', MK.binders.innerText()); this.x = 'Some <i>Text</i>'; //    " ”,   
      
      





MK.binders.style-オブジェクトのプロパティを要素のstyle



オブジェクトのプロパティに関連付けます。



 this.bindNode('x', '.my-node', MK.binders.style('color')); this.x = 'red'; //     
      
      





そして最も興味深いのは、 MK.binders.fileです。 この新しいバインダーは、 input[type=”file”]



の内容を変更するユーザーをキャッチするだけでなく、必要な形式でファイルを読み取ります。



 this.bindNode('x', '.my-file', MK.binders.file('dataURL')); //   ,    this.on('change:x', function() { console.log(this.x.readerResult); // "data:image/png;base64,iVBO..." });
      
      





詳細については、 バインダードキュメントを参照してください。



innerHTMLclassNameプロパティ 、および属性 バインダーの getValue



ここで、前述のバインダーを使用して、Matryoshkaカーネルは現在のプロパティに対応するプロパティがあるかどうかをチェックし、答えがいいえの場合、要素から値を抽出してプロパティに割り当てます。



 <div class="my-div">Some data</div>
      
      





 // ,  this.x  . this.bindNode('x', '.my-div', MK.binders.innerHTML()); alert(this.x); //"Some data"
      
      







onItemRender



Matreshka.Array



は、新しい仮想onItemRender



メソッドがあります。 コレクション内の要素の1つが描画されると呼び出されます。 このメソッドは、コードをよりフラットにし、 "render"



イベントをリッスンするのを防ぎ"render"







"render"



イベントは常に標準パターンであるため、レンダリング時に必要なバインディングを追加できます。



 class MyCollection extends MK.Array { Model: MyModel; itemRenderer = '<li>'; constructor() { super() .bindNode('sandbox', '.array-sandbox') .on('*@render', evt => { evt.self.bindNode(...); }); } }
      
      





これで次のことができます。



 class MyCollection extends MK.Array { Model: MyModel; itemRenderer = '<li>'; constructor() { super() .bindNode('sandbox', '.array-sandbox'); } onItemRender(item, evt) { item.bindNode(...); } }
      
      





「モデル」には、同様の仮想メソッドonRender



ます。

以前は次のようでした。



 class MyModel extends MK.Object { constructor() { super() .on('render', evt => { this.bindNode(...); }); } }
      
      





これで、次のように書くことができます。



 class MyModel extends MK.Object { constructor() { super() } onRender() { this.bindNode(...); } }
      
      







ノード$ノードのプロパティ



データバインディングおよびDOMノードを宣言した後、開発者はバインドメソッドと$バインドメソッドを使用して接続されたノードにアクセスできます。 bound



は、最初のバインドされた要素$bound



-jQueryまたはBalalaikaコレクションの形式のすべての要素を返します。



 this.bindNode('x', '.my-node'); var boundNode = this.bound('x'); var allBoundNodes = this.$bound('x');
      
      





nodes



$nodes



のプロパティを使用すると、同じことを行うことができますが、これらのプロパティは通常のオブジェクトであるため、パフォーマンスの点ではほとんど無料です。



 this.bindNode('x', '.my-node'); var boundNode = this.nodes.x; var allBoundNodes = this.$nodes.x;
      
      







いくつかの新しいメソッド



Matreshka.toは 、任意のオブジェクトをMK.Object



およびMK.Array



インスタンスに変換します。

MK.Array.ofはArray.ofと同じように機能しますが、 MK.Array



インスタンスを返します。

MK.Array.fromはArray.fromと同じように機能しますが、 MK.Array



インスタンスを返します。

String.prototype.trimをサポートしないブラウザーのMK.trim

MK.toArrayは、配列のような配列をネイティブ配列に変換する速度がArray.prototype.sliceの 2倍です。



性能向上



for..in



(たとえば、 each



関数の代わりにfor..in



ループを使用)とより大きな変更の助けを借りて、優れた結果を達成することが判明しました。 たとえば、小さなコレクション(10要素)のベンチマークでは、ChromeとFirefoxでマトリョーシカがReactに10〜20%遅れました(ただし、多数の要素を含むベンチマークのコレクションを追い越しました)。 同じテストで、マトリョーシカはChromeのReactより50%高速で、Firefoxの3倍高速です。



以下に、 10個の要素50個の要素100個の要素500個の要素1000個の要素のベンチマークを示します。



バグの処理:テスト



マトリョーシカは最終的に自動的にテストされます。 このドキュメントの執筆時点では、メソッドが開発ブランチに入る前にテストする148のテストが実装されています。 特に細心の注意を払ってテストされているのは、さまざまな状況下で機能し、同時に何かを壊さないために必要な委任されたイベントです。



ブラウザのサポート



ドキュメントサイトには、このバージョンのロバに実装できないメソッドが大量にあるため、Internet Explorer 8でのマトリョーシカの使用は推奨されないという警告が表示されました。 実際、これは開発者がそのような方法を考えずに使用しようとした場合の免責事項にすぎません。 覚えておかなければならないのは、ネイティブオブジェクトに「魔法」を追加する静的メソッドがIE8で機能しないことです。



this



がMatryoshkaのインスタンスであれば、そのようなコードはIE8で動作します。



 this.bindNode('key', '.node');
      
      





そして、これは動作します:



 var mk = new Matreshka(); mk.bindNode('key', '.node');
      
      





そして、このコードはIE9 +および他のブラウザー(古代のWebKitおよびOpera Miniを含む)でのみ機能します。



 var object = {}; MK.bindNode(object, 'key', '.node');
      
      





8番目のロバで静的メソッドを使用するために手が非常にかゆい場合は、最初にオブジェクトをMatryoshkaのインスタンスに変換できます。



 var object = MK.to({}); MK.bindNode(object, 'key', '.node');
      
      





したがって、 セマンティックバージョニングが尊重されます。



その他の変更



-bindNode構文少し拡張されました。

bindNode



メソッドを使用したノードの欠落に関するエラーbindNode



より情報量bindNode



多くなりました。キー以外のテキストでは、セレクターが示されます(セレクターが渡される場合)。

-ソースコードは小さなコンポーネントに分割されます。

-不要なスペース( f(x)



代わりにf( x )



)がコードおよびサイトの例から削除されました。

-前述のように、マトリョーシカはOpera Miniと古いWebKitをサポートしています。



他の変更や修正されたエラーのリストについては、サイトの対応するセクションで確認できます。



その他



Trelloで行われたことと行われる予定を確認できます。 投票することもでき、カードの優先度が上がります。



Gitterチャットでは、新機能に関する議論が頻繁に行われます。 このため、ユーザーが尋ねる質問とその回答は、インターネットの暗い地下室のどこかで失われます。 したがって、実験として、 Muutロシア語英語 )に基づくフォーラムを開始することが決定されました。 質問が発生した場合は、(質問が愚かだと思っていても)気軽に質問してください。



たとえば、 Rendolのすばらしい質問の1つと、それに対する詳細な回答があります。

ご挨拶!

再び以前と同じ質問をしましたが、その答えはまだ美しいとは思いませんでした。

たとえば、VKのような対応:

 User = { id: 5, name: 'Fedor', online: true }
      
      





このユーザーを別の部屋に配置します:room1、room2、room3。

User.online = falseの場合、3つの部屋すべてで、たとえば色を変更する必要があります。

T.O. 1つのオブジェクトを含む3つのコレクションで、同時にこのオブジェクトは3か所に表示されます。

注:これらのコレクションが同じタイプ(部屋だけでなく)である必要はなく、異なるタイプや表現であってもかまいません。

単一のオブジェクトを複数のビューにバインドすることは可能ですか?


答えは:

はい、オブジェクトを含むコレクションは異なる場合があります。 bindRenderedAsSandbox: false



, , (, , ). . render



, , , .

: jsbin.com/cidise/12/edit . user



( ul), -. tableUsers[0].name = 'xxx'



, , . , .

脅威。 IE 8, , , .instanceOf





 object.instanceOf( MyClass ); //  object instanceof MyClass
      
      







, . ご清聴ありがとうございました。



All Articles