Habrのすべての読者と作家への挨拶。
アプリケーションの構造化の外観をわずかに変更できるコンパクトなフレームワークを紹介します。 マトリョーシカにはちょっとした魔法が埋め込まれていますが、これは次のような一連の記事で明らかにすることができます。
注目を集めるコード:
mk.on( 'change:x', function() { alert( 'x is changed to ' + this.x ); }); mk.x = 2; // alerts "x is changed to 2"
そしてそれは... IE8で動作します。
マトリョーシカとは?
フレームワークとしてのマトリョーシカ
コンパクトなサイズと学習しやすいアーキテクチャにより、大規模で拡張可能なアプリケーションを構築できます。 今日は誰も驚かないでしょうが、試してみます。
図書館としてのマトリョーシカ
Matryoshkaが提供する機能が気に入ったら、コードを変更する必要はありません。 マトリョーシカは、興味深いメソッドを持つクラスのセットとして使用できます。
独自のフレームワークを作成するためのプラットフォームとしてのマトリョーシカ
マトリョーシカは、MVC、MVVM、%your_design_pattern%フレームワークとして位置付けられていない拡張可能な汎用フレームワークです。したがって、プログラマーは、設計パターンの望ましいセットを使用できる独自のアーキテクチャを実装する機会があります。
なんで?
プレゼンテーションについて考え、自分が書いたコードでプレゼンテーションを変更することにうんざりしています。 Javascriptでのデータ同期とプレゼンテーションのすべての松葉杖は、私に否定的な感情を呼び起こします。マトリョーシカの将来に対する野心的な目標は、データのみで動作するUIがあることを完全に忘れることです。 もちろん、このタスクを100%解決することはできませんが、私たちプログラマーは、与えられたツールを最大限に活用して、コードをより簡潔に、より短く、より柔軟にする必要があります。 コードを振って悪になる時です。
注意GIF
データバインディング
ブラウザのクライアントアプリケーションでは、データとプレゼンテーションの同期が必要であることが知られています。 古典的な問題:データがあります:
var o = { x: 2 };
このデータを変更する
select
があります:
<select> <option>1</option> <option>2</option> <option>3</option> <select>
純粋なJS + jQueryコードを使用する場合、次のように記述します。
$( 'select' ).on( 'change', function() { ox = +this.value; });
次に、データを変更する場合、次のように記述します。
ox = 1; $( 'select' ).val( ox );
これは、プロパティと要素という2つの「原子」の値を一度に設定する必要があるため、あまり良くありません。
この問題はさまざまな方法で解決されます。バックボーン(MVC)は、アプリケーションをモデル(モデル、コレクション)、プレゼンテーション(HTMLコード)、およびコントローラー(ビュー)に分割します。 Knockout and Angular(MVVM)は、.NETからクライアントWebに移行したプラクティスを使用してデータをバインドします:コントローラー(この場合はViewModel)を置き換えるロジックの一部はHTMLコードで記述されています(説明が間違っている場合は謝罪します)。 追いつかないフレームワークがたくさんあります。
マトリョーシカは、最初にプログラマーが(MVVMとは異なり)HTMLコードに触れないようにプレゼンテーションにデータを添付します。次に、プログラマーは(Backboneとは異なり)モデルを変更するHTML要素のイベントについて考えることを止めます。
var mk = new Matreshka; // new MK; , MK === Matreshka, mk.bindNode( 'x', 'select', { on: 'change', getValue: function() { return this.value; }, setValue: function( v ) { this.value = v; } });
データを変更する場合は、次のように記述します。
mk.x = 2;
このようなコードはXをデュースに割り当てるだけでなく、
.value = 2
設定することでselectの状態を変更します。
そして、これは
.set
ようなメソッドを使用し
.set
。
jsbin.com/jikewipi/2/edit
ここで何が起こったのですか?
mk.bindNode( 'x', 'select' ... )
奇妙なことに、
bindNode
メソッドは、要素をプロパティにバインドします。 最初の引数はオブジェクトのキー、2番目はセレクター、この場合は
'select'
です。 2番目の引数のタイプは、jQueryで受け入れられる任意の値です:セレクター、純粋な要素、jQueryオブジェクト、NodeList、要素の配列...つまり、
'select'
は
$( 'select' )
または
document.getElementsByTagName( 'select' )
または
document.querySelector( 'select' )
置き換えることができ
document.querySelector( 'select' )
。
3番目の引数は最も興味深いので、順番に分析します。
on: 'change'
質問に答えます。「要素から値を取得し、対応するプロパティに割り当てるために、要素でどのイベントが発生する必要がありますか?」
getValue: function() { return this.value; }
「要素の値を抽出する方法」という質問に答えます。値を返します。
setValue: function( v ) { this.value = v; }
「要素の値をどのように設定しますか?」という質問に答えます。vは値です(この場合は2
)。
ドックへのリンク: finom.github.io/matreshka/docs/Matreshka.html#bindElement
タスクを複雑にしましょう。 jQuery UIからスライダーapi.jqueryui.com/sliderがあり、それをデータにバインドするタスクがあったとします。 ドキュメントを見て、スライダーがドラッグされたときに発生する
"slide"
イベントを確認します。
まず、バインドする前に、スライダーを宣言します。
<div class="slider"></div>
$( ".slider" ).slider({ min: 0, max: 100 });
次に、マトリョーシカのインスタンスを宣言します。
var mk = new Matreshka();
次に、バインディングを呼び出します。
mk.bindNode( 'x', '.slider', { on: 'slide', // , getValue: function() { return $( this ).slider( 'option', 'value' ); // (. jQuery ui.slider)? }, setValue: function( v ) { $( this ).slider( 'option', 'value', v ); // (. jQuery ui.slider)? } });
次のコードを呼び出すと...
mk.x = 44;
...スライダーの位置が変わります。
逆に、スライダーハンドルをドラッグすると、
mk.x
が変更されます。
UPD :バージョン0.2以降、3番目の引数に新しいキー
'initialize'
が追加されました。これにより、バインドする前に要素を初期化できます。 0.2に関する記事を参照してください 。 コードは次のようになります。
mk.bindNode( 'x', '.slider', { initialize: function() { $( this ).slider({ min: 0, max: 100 }); }, on: 'slide', getValue: function() { return $( this ).slider( 'option', 'value' ); }, setValue: function( v ) { $( this ).slider( 'option', 'value', v ); } });
値を表示するHTML要素を追加して、タスクを少し複雑にしましょう。
<div class="output">Value is <span class="x"></span></div>
そして、それを
'x'
バインドします:
mk.bindNode( 'x', '.output .x', { setValue: function( v ) { this.innerHTML = v; } });
ご覧のとおり、要素のバインディングオプションには
"on"
プロパティと
"getValue"
メソッドがありません。 これは、
".output .x"
要素から値を抽出せず、設定するだけであることを意味します。
複数の要素を1つのプロパティにバインドできます。 逆もまた真です。1つの要素に多くのプロパティをアタッチできます。 マトリョーシカは、「多対多」のルールに従って要素をバインドできることを覚えておいてください。
結果: jsbin.com/bulobuhu/7/edit (「コンソール」タブを開いて
x
変更してみてください
mk.x = 42;
たとえば、
mk.x = 42;
と書き
mk.x = 42;
)
素晴らしく、2つの要素をバインドしましたが、冗長なコードがたくさんありました(私の意見)。 スライダーがたくさんある場合はどうなりますか? 私が書くたびに...
mk.bindNode( property, element, { on: 'slide', getValue: function() { return $( this ).slider( 'option', 'value' ); }, setValue: function( v ) { $( this ).slider( 'option', 'value', v ); } });
...あまりきれいではありません。
どうする 要素をバインドするときにチェックされる要素の一般的な機能を覚えておく必要があります。 これを行うと便利です。
mk.bindNode( 'x', '.slider' );
どうやってやるの?
MK.elementProcessors
MK.elementProcessors
MK.defaultBinders
(名前変更)があります。これは関数の配列です。 関数は、チェック対象の要素である引数
el
取り、条件を含みます。要素が何らかのルールに一致する場合、上記の
.bindNode
例では3番目の引数であるオプションオブジェクトを返します。
スライダーには1つのルールがあります。誰もが
ui-slider
クラスを持っています。 したがって、条件関数は次のようになります( UPD :
'initialize'
プロパティを忘れないでください):
function( el ) { if( $( el ).hasClass( 'ui-slider' ) ) { return { on: 'slide', getValue: function() { return $( this ).slider( 'option', 'value' ); }, setValue: function( v ) { $( this ).slider( 'option', 'value', v ); } }; } }
例は、非常に単純であることを望みます。要素がスライダーであるかどうかを確認し、スライダーである場合はオプションを返します。
関数を
MK.defaultBinders
配列に挿入します。
MK.elementProcessors.push( function( el ) { if( $( el ).hasClass( 'ui-slider' ) ) { return { on: 'slide', getValue: function() { return $( this ).slider( 'option', 'value' ); }, setValue: function( v ) { $( this ).slider( 'option', 'value', v ); } }; } });
ドキュメントリンク: finom.github.io/matreshka/docs/Matreshka.html#defaultBinders
その後、少なくとも100個のスライダーを使用して、
.bindNode
関数で、
.bindNode
するプロパティと要素のみを定義します。
<div class="slider1"></div> <div class="slider2"></div>
$( ".slider1, .slider2" ).slider({ min: 0, max: 100 }); var mk = new Matreshka(); mk.bindNode({ x1: '.slider1', x2: '.slider2' });
結果: jsbin.com/celarefu/2/edit
例では、出力が参照によってバインドされていることに注意してください。
mk.bindNode({ x1: '.output .x1', x2: '.output .x2' }, MK.binders.innerHTML() );
MK.binders
は、上記のオブジェクトを返すカスタムバインド関数を含む静的プロパティです(プロパティ
on, setValue, getValue
)。 オブジェクトは、独自のバインダーで展開できます。
MK.binders.innerHTML
通常のhtml要素のバインディングオプションを返し、その
innerHTML
変更する関数。
Doc: finom.github.io/matreshka/docs/Matreshka.binders.html#innerHTML
他のルールと一致しない要素の
.innerHTML
を変更するルールを作成してみませんか? 一言で言えば、利便性です。 値が不要な要素をバインドする必要がある場合もありますが、それについては次の記事で詳しく説明します。
デフォルトでは、
MK.defaultBinders
は、単純なルールへの準拠について要素をチェックする1つの関数が含まれ、単純な要素(
select
、
textarea
、
input[type="text"]
、
input[type="checkbox"]
、
input[type="radio"]
)。
例えば
<select class="my-select"> ... </select>
次のようなコードを使用します。
mk.bindNode( 'x', '.my-select' );
...ネスト人形自体は、最も一般的なselect
ため、いつ、どのように取得し、どのように要素の値を設定するかを知ってい
select
。
例: jsbin.com/tepiyoso/2/edit (クリックして、入力のテキストを変更し、ソースを見てください)
クラスセレクターを使用したクラス基準
2日前、マトリョーシカの発表をjavascript.ruフォーラムに投稿しました。 ユーザーnerv_は、AngularJSコードを引用して、セレクターアプローチの批判を合理的に説明しました。
添付する要素が多数ある場合、添付する要素のクラスを変更しないように注意する必要があることに同意します。したがって、この場合、UIでキーを指定すると機能する場合があります。
木製の方法:
正しい方法(
に関する記事の
メソッドの
):
これらの2つの方法は、MVVMパターンの最初のステップと呼ばれ、マトリョーシカに基づいて実装できます。 証拠は、プラグインとそれに関する記事の形で準備されています。
アプリケーションの「複雑な」要素を、各要素が基本クラス(つまり、継承に関する記事でより明確になる)で、多くのバインディングがないように区別することを好みます。 おそらく、私のアプローチは、JSコードとHTMLは可能な限り異なるべきだという信念によって大きく変形します。タイプセッターの植字と、プログラマーが行う場合は、最小限の必要な変更のみです。
少なくともサービスセレクタを監視する必要があることがわかります。
そして、すべての「バインディング」をjsコードで記述する必要があることがわかりました。 そして、原則として、宣言的に尋ねる代わりに-安くて元気に-彼らの多くがあります。
添付する要素が多数ある場合、添付する要素のクラスを変更しないように注意する必要があることに同意します。したがって、この場合、UIでキーを指定すると機能する場合があります。
<form> <select data-key="a"></select> <select data-key="b"></select> </form>
木製の方法:
$( '[data-key]' ).each( function() { this.bindNode( this.getAttribute( 'data-key' ), this ); });
正しい方法(
.each
に関する記事の
.each
メソッドの
MK.Object
):
this.each( function( v, key ) { this.bindNode( key, this.$( '[data-key=' + key + ']' ) ); });
これらの2つの方法は、MVVMパターンの最初のステップと呼ばれ、マトリョーシカに基づいて実装できます。 証拠は、プラグインとそれに関する記事の形で準備されています。
アプリケーションの「複雑な」要素を、各要素が基本クラス(つまり、継承に関する記事でより明確になる)で、多くのバインディングがないように区別することを好みます。 おそらく、私のアプローチは、JSコードとHTMLは可能な限り異なるべきだという信念によって大きく変形します。タイプセッターの植字と、プログラマーが行う場合は、最小限の必要な変更のみです。
イベント
Matryoshkaには、Backbone.jsと同じように機能するイベントジェネレーターが含まれています。 最初は、フレームワークはユーザーのニーズに合わせて作成されていたため、イベントを担当するコードの一部はこのフレームワークから借用されていました。 しかし、これらのメソッドのコードは非常に安定していることが判明したため、そのままにしておくことにしました。
var mk = new Matreshka(); mk.on( 'someevent', function( a, b ) { alert( a + ', ' + b ); }); mk.trigger( 'Hello', 'World!' ); // alerts "Hello, World!"
それはおなじみですか?
ドキュメントリンク: finom.github.io/matreshka/docs/Matreshka.html#on
投稿の冒頭で述べたおいしい機能の1つは、プロパティの変更イベントの追跡です。
var mk = new Matreshka(); mk.on( 'change:x', function() { alert( 'New x value is ' + this.x ); }); mk.x = 5; //alerts "New x value is 5"
プロパティをいくつかの要素にバインドし、その値の変化を追跡することもできます。
<select class="my-select"> <option>1</option> <option>2</option> <option>3</option> </select>
var mk = new Matreshka(); mk.bindNode( 'x', '.my-select' ); mk.on( 'change:x', function() { alert( 'x is now ' + this.x ); });
アプローチには大きな違いがあります。以前は要素のDOMの変化を追跡していましたが、データの変化を追跡するようになりました。データは私たちにとってのみ重要であるため、要素の状態はそれ自体で変化します
自分で試してください: jsbin.com/dadakeba/1/edit
.set
メソッドの使用
.set
メソッドは、単に指定されたプロパティに値を割り当てます。 次の2つの目的で使用されます。
1.プロパティを
change:*key*
渡します
change:*key*
イベントオブジェクト(
"silent"
フラグなど)。 UPD:さらにフラグがあります 。
mk.on( 'change:x', function( evtOpts ) { alert( evtOpts.myFlag ); }); mk.set( 'x', 5, { myFlag: 'blah' } ); // "change:x" mk.set( 'x', 42, { silent: true } ); //
silent: true
フラグが渡されても、バインドされた要素の値は変更されることに注意してください。
2.コードの略語。 プロパティを持つオブジェクトを
.set
メソッドに渡すことができます。
mk.set( { x1: 1, x2: 2 } ); mk.set( { x1: 3, x2: 4 }, { silent: true } );
入手先
githubのリポジトリ: github.com/finom/matreshka (フレームワークのコードは
build/
フォルダーにあります)
結論として
マトリョーシカに搭載されているメインチップを紹介しました。 他の機能はドキュメントにあります 。 ここには、他の同様に重要な方法があります。
.off
指定されたイベントを無効にする
.remove
削除プロパティ
ぶら下げカスタムアクセサーを
.define
.defineGetter
要素にゲッターをぶら下げ
.bound
、
.boundAll
、プロパティにバインドされた要素または要素のコレクションをそれぞれ返す
.unbindElement
は、プロパティと要素の間のリンクを
.unbindElement
します
...など。
バージョン0.0.Xを使用する理由
このプロジェクトは、別の名前で1年以上開発されているという事実にもかかわらず、初めて(この記事で)初めて紹介されたからです。
次は?
次の記事では、マトリョーシカを単に便利な関数のセットとしてではなく、クラスに基づいた本格的なフレームワークとして使用して、最初のアプリケーションを作成する方法を説明し、クラスが正確である理由を説明します(この問題に関する地元のinりに精通しています)。
次に、データを処理する
MK.Array
および
MK.Object
について説明します。
その後、読者が興味を持っているなら、魔法の仕組みと、IE8で
Object.defineProperty
サポートを実装する方法を説明します。
最後まで記事を読むことができたすべての人に感謝します。 幸運と成功したコーディング。