
- Matreshka.js:シンプルからシンプルへ
- マトリョーシカを試す10の理由
- Matreshka.js 1.1:さらにクール
- Matreshka.js:イベント
ロシア語のドキュメント
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セクションにリストされています。
マトレシュカマジックライブラリー
ネイティブオブジェクトのサポートのおかげで、すべての「マジック」関数を、 Matreshka 、 Matreshka.Array 、 Matreshka.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]);
on 、 once 、 onDebounceメソッドでのイベント型オブジェクトのサポート
以前のバージョンでは、開発者はイベントハンドラーを宣言するための唯一の構文を使用できました。
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..." });
詳細については、 バインダーのドキュメントを参照してください。
innerHTML 、 className 、 プロパティ 、および属性 バインダーの 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
, . ご清聴ありがとうございました。