Habréに関する私の以前の出版物はその目標を達成しました-多くの人々がW3Viewの存在について学び、何人かはGitHubを訪れました。
同時に、次のコメントは、このライブラリが解決する問題、それがどのように、そしてなぜあなたがそれを必要とするかもしれないのかについてのより明確な説明の必要性を明らかにしました。
何らかの理由で、痛み、吐き気、めまいを経験することなく、Webテクノロジーで開発されたUIを構築する方法を学ぶことに興味がない場合、この記事は退屈に思えます。残りは猫にお勧めします。
パターン、伝統、および「パターン」
1つの小さなinnerHTMLによって確立された伝統によると、WebのUIはさまざまなテンプレートエンジンを使用して構築されています。過去数年間、テンプレートエンジンは大きく進歩し、不可能に発展しました。 インターフェースの作成を単純化する必要がありましたが、タスクはそれを容易にしませんでした。 複雑な問題を解決するために、テンプレートエンジンの開発者は多くの新しい概念を導入し、さまざまな概念を考案したコスタからつながりました 。 いくつかの概念は、モデルを構築するためのルールを規定しますが、これは私の意見では常識に完全に反しています。 コンセプト用のアプリケーションを作成していません。 同時に、テンプレートエンジンは、DOM APIを使用する必要性を誰からも解放するわけではありませんが、すべてを予測することは不可能です。 ただし、DOMへの直接的な影響は、最も美しい概念でさえも簡単に破ることができます...
テンプレートエンジンの開発者は、概念の破壊を防ぐために、そのような嫌悪感と悪いマナー 、つまり私たちの意見ではアンチパターンを宣言しました。
開発者がテンプレートに適合しない場合、開発者は何をしなければなりませんか? -ひとつのことが残っている- 代替手段を探すこと。
パターンの代替
むかしむかし、HTML、Javascript、およびDOM APIにより、テンプレートとブラックマジックを使用する必要がなくなりました。 HTMLは、DOM APIを使用してフォームの構造であるJavascriptを記述し、このフォームを制御し、そのイベントを処理しました。美しく、シンプルで便利でした。 しかし、インターフェースはより複雑で動的になりつつあり、HTMLは引き続きデータ記述言語です。 分解は、大規模で複雑な形状を小さな独立した再利用可能なカプセルに分解するコンポーネントである可能性があります。
コンポーネント宣言は、データの変更に対する反応を説明し、イベントハンドラを定義するスクリプトを備えた小さなHTMLページのように見える場合があります。 次に、これらのコンポーネントから、より複雑なコンポーネントを組み立てることができます。
- どこで入手できますか? -工場から。
- 彼らはどうやってそこに着くのですか? -あなた自身がそれらを説明し、そこに配置します。
- それらをどのように説明しますか? -通常-HTML、Javascript、およびDOM API。
- そのような工場はどこで入手できますか? -npm install w3view 。
W3Viewは、単純なコンポーネントと複雑なコンポーネントを作成するためのファクトリです
小さなアプリケーションの例を使用して、これがどのように機能し、どのように使用するかを説明しようとします。 W3View•TodoMVCに基づいて、どの例を検討するのがよいかについて長い間考えていました 。 完了し、動作し、実証するために行われました。比較するものは何もありません。
仕様、レイアウト、その他の受信トレイについては、 こちらをご覧ください 。 さらに読む前に、仕様とレイアウトを一見する価値があるでしょう。
W3Viewアプリの仕組み•TodoMVC
モデルとコントローラーの説明は省略しますが、これらは目立たないものです。 アクティブなモデルが内部ロジックを実装し、表示方法について何も仮定せず、Viewは完全にW3Viewに基づいており、Controllerはすべてを最終アプリケーションにバインドする、古典的なMVCトライアドを使用したとしか言えません。 アプリケーションは、変更せずにTodoMVCレイアウトを実装し、そのスタイルを追加しません。
簡単にするために、コンポーネントの説明は、ページのすぐ上の非表示のDIVに配置されます。
アプリケーションの接続、初期化、起動
アプリケーションは、4つのSCRIPTタグを使用してページに接続します。
<script src="node_modules/w3view/w3view.js"></script> <script src="js/models/todo.js"></script> <script src="js/controllers/todo.js"></script> <script src="js/app.js"></script>
1つ目はW3Viewライブラリー、2つ目と3つ目はモデルとコントローラー、4つ目は起動スクリプト、その中の主なもの(W3Viewの作業に関連するものから)は次のとおりです。
var appContext = new todoController(todoModel('todos-W3View')); var w3view = new W3View(appContext); var sources = document.getElementById('components'); w3view.register(sources.children); document.body.removeChild(sources); w3view.create('application').mount(document.body, 0); // ,
- コントローラーとモデルを初期化します。
- W3Viewのインスタンスを作成し、コントローラーで初期化します。何でも初期化できます。
- コンポーネントの説明を取ります。それらはDIV#コンポーネントに配置されます。
- W3Viewファクトリにコンポーネントを登録します。
- DOMツリーからコンポーネントの説明を削除します-あなたはそれを残すことができますが、なぜそこにあるのですか? -工場に入った後、彼らはもはや必要ありません。
- APPLICATIONのルートコンポーネントのインスタンスを作成し、最初の要素でページにマウントします。
- 出来上がり、アプリケーションは実行中です!
通常、私はより少ない行でうまくいきますが、ここでは美しさのためにDOMツリーをきれいにすることにしました、何をすべきかは完璧主義です。 事前に準備されたバンドルを使用する場合、これらの6行は2行になります。
var appContext = new todoController(todoModel('todos-W3View')); w3view(appContext).create('application').mount(document.body, 0);
もちろん、バンドルは本番環境でははるかに優れていますが、開発および明確化のために、ソースを接続することを好みます。
接続、初期化、起動しましたが、何ですか? はい...コンポーネントを作成する必要がありました!
コンポーネントを作成する
既に述べたように、コンポーネント宣言はHTML要素であるため、既存のレイアウトを断片に分割し、データの変更とイベントハンドラーへの反応を記述し、それを元に戻すだけで十分です。
TodoMVCでは、動的部分を簡単に区別でき、 APPLICATIONのルートコンポーネントは次のようになりました。
<section as="application" class="todoapp"> <header class="header"> <h1>todos</h1> <input ref="newTodo" class="new-todo" placeholder="What needs to be done?" autofocus> </header> <main ref="main"></main> <totals ref="totals"></totals> <script type="javascript"> // , // </script> </section>
ご覧のとおり、これは単なるHTMLであり、いくつかの非標準属性、2つの非標準タグ、および間違ったSCRIPTタグタイプがあります。
- asおよびrefの非標準属性、宣言されているコンポーネントの名前、およびその内部要素を参照するラベル。 これらの属性は次のように解釈されます。
<section as="application" ...>... SECTION APPLICATION, <input ref="newTodo" ...> INPUT, "this.ref.newTodo"
コンポーネントインスタンス自体は、 このように独自のスクリプトからアクセスできます。これは、コンポーネントインスタンスが単なるDOMノードである方法です。
非標準のMAINタグとTOTALSタグは(ご想像のとおり)同じ動的パーツであり、個別のコンポーネントに選択され、アプリケーションにタグとして挿入されます。 後で検討します。
- 自動実行を防ぐ必要がある場合は、間違ったタイプのSCRIPTタグを使用する必要があります。 これは、ページに直接説明を配置したためです。 コンポーネント記述のスクリプトの本体はコンストラクター関数の本体であり、作成後に各インスタンスに対して実行されます。 この関数は次のようになります。
function (appContext, factory){ // }
- appContextは、W3Viewファクトリを初期化したのと同じappContextです。
- factoryは、コンポーネントインスタンスを作成したファクトリのインスタンスです。
SCRIPTタグのコンテンツとコンポーネントインスタンスのライフサイクル
だからスクリプト。 Controllerに送信について伝える必要があります。アプリケーションをページに配置するときにこれを行います。そのため、スクリプトでonMountハンドラーを定義します。
this.onMount = function (){ appContext.setView(this); console.log("application section created"); }
このハンドラーは、コンポーネントがDOMツリーに挿入(マウント)された直後に呼び出されます。 コンポーネントがアンマウントされる直前に呼び出される、ペアのonUnmountを定義できます。 それらが機能するためには、コンポーネントの特別なメソッド-mount(targetElement、index)およびunmount()を使用してマウントおよび逆アセンブルする必要があります。 コンポーネントがDOMから削除された場合、必要に応じて再度マウントできます。 また、コンポーネントを作成および破棄するときに呼び出されるonCreateおよびonDestroyもあります( destroyメソッドを使用してコンポーネントを破棄する必要があります)。 コンポーネントインスタンスでdestroyが呼び出されると、最初にアンマウントされ(unmount())、次にサブツリー全体がゴミ箱に再帰的に破棄されます。 destroyを呼び出した後、アイテムは使用できなくなります。
他のオブジェクトにコンポーネント参照を配置する必要がある場合は、 onCreateハンドラとonMountハンドラを定義する必要があります。 これらのリンクを削除するコードはonDestroyハンドラーとonUnmountハンドラーに配置する必要があります 。これは対称的に行うことが望ましいです。
// TodoMVC // , function someFunc(e){...} // -, // // this.onMount(){ window.addEventListener('mousemove', someFunc); } // ? // this.onUnmount(){ window.removeEventListener('mousemove', someFunc); }
手を洗うだけで、メモリが漏れることはありません。
SCRIPTタグの内容、データの更新
そのため、ライフサイクルを把握しました。次に、データの変更に対処します。
<section as="application" class="todoapp"> <header class="header"> <h1>todos</h1> <input ref="newTodo" class="new-todo" placeholder="What needs to be done?" autofocus> </header> <main ref="main"></main> <totals ref="totals"></totals> <script type="javascript"> this.onSetData = function (input){ this.ref.main.setData(input); this.ref.totals.setData(input.total); }; this.onMount = function (){...}; </script> </section>
ここではすべてが簡単です。新しいデータを受信すると、それらをサブツリーの要素に配布します。 外部から(ここではコントローラーから)データはsetDataメソッドを使用して設定され、W3Viewコンポーネントの作成されたすべてのインスタンスに既に存在し、単に決定する必要があるonSetDataハンドラーを呼び出します。 setDataメソッドとonSetDataハンドラーは、最大3つの引数を取ることができます。
- データ -レンダリングのための実際のデータ-主な引数、通常はそれだけで十分ですが、時には他のものが役立つ場合があります。
- opts-困難な場合、たとえばリストを操作する場合、リストのすべての要素に共通する追加のコンテキストを伝える必要があります。
- additional-追加属性。リストの場合、リスト内のアイテム番号がここに表示される場合があります。
また、他の問題では、3つの引数すべてを自由に適用し、必要なすべてを渡すことができます。これは、解決されるタスクとonSetData ハンドラーの実装のみに依存します。
this.ref.mainおよびthis.ref.totalsは、コンポーネントのサブツリー内の要素へのリンクであり、属性ref - ref = "main"およびref = "totals"でそれぞれマークされています。
SCRIPTタグの内容-イベントを処理する方法
APPLICATIONコンポーネントにはもう1つの要素があり、 ref = "newTodo"というラベルが付けられています-これは入力フィールドです。 仕様によれば、何でも入力できますが、ENTERキーを押すと、これがバグのリストにヒットするはずです。 当然、これを実現するには、Enterキーを押す必要があります。
W3Viewのイベントは、非常に標準的な方法で処理されます-通常の要素に非常に標準的なイベントハンドラーをフックすることによって。 この場合、属性ref = "newTodo"を設定した要素のonkeydown 。
<section as="application" class="todoapp"> <header class="header"> <h1>todos</h1> <input ref="newTodo" class="new-todo" placeholder="What needs to be done?" autofocus> </header> <main ref="main"></main> <totals ref="totals"></totals> <script type="javascript"> this.ref.newTodo.onkeydown = function (e){ var ENTER_KEY = 13; if(e.which !== ENTER_KEY) return; appContext.addNewTodo(this.value); this.value=''; }; this.onSetData = function (input){...}; this.onMount = function (){...}; </script> </section>
すべてが非常に普通です:要素を取り、イベントをハングさせ、ENTERキーが押された場合、コントローラーのメソッドを呼び出します(ここのコントローラーはappContextと呼ばれます- 新しいW3Viewに渡しました -これです)。
アプリケーションのルートコンポーネントは終了しました。次のコンポーネントに移りましょう。 TOTALSコンポーネントは簡単なので、次のものはMAINになります。これについては何も書きません。 DOM要素への属性およびプロパティの設定とまったく同じで、新しいものはありません。 ただし、コンポーネントMAINはより詳細に停止する必要があります。
組み込みのARRAY-ITERATORコンポーネント
MAINで最も興味深いのは、組み込みのARRAY-ITERATORの使用です。
... <array-iterator ref="list" usetag="ul" class="todo-list"> <listItem></listItem> </array-iterator> ...
ARRAY-ITERATORはW3Viewのすべてのインスタンスに既に存在し、このコンポーネントは配列を表示するために使用されます。 要素を表示するコンポーネントによって構成されます。 setDataでは、 ARRAY-ITERATORは2つの引数を取ります。
- データ - データ配列
- optsは、配列のすべての要素に共通するものです。
配列要素を表示するコンポーネントは、3つの引数を受け取ります。
- item-配列の要素
- opts -ARRAY-ITERATORに転送したすべてに共通するもの、
- index-配列内の要素のインデックス。
配列要素を表示するために複数のコンポーネントを指定すると、そのインスタンスが交互になります。たとえば、
... <array-iterator ref="list" usetag="ul" class="todo-list"> <listItem class="even"></listItem> <listItem class="odd"></listItem> </array-iterator> ...
この場合、すべての奇数要素にはクラス「even」があり、すべての偶数要素にはクラス「odd」があります。
ARRAY-ITERATORはDIVに基づいていますが、ここではULが必要なので、 usetag = "ul"属性で変更します。 W3Viewのすべてのkm3コンポーネントは、そのような属性を受け取り、宣言に基づいてタグを置き換えることができます。
コンポーネントを宣言するときにTABLEコンテキストの外部でTBODY、TR、またはTDタグを使用する必要がある場合は、W3Viewコンポーネント以外のアイテムにtagname属性を使用できます。
さて、これでコンポーネントを作成する方法、ファクトリにコンポーネントを登録する方法、およびコンポーネントを適用する方法を理解できました。 コンポーネントを手に持つ方法について話すだけです。
コンポーネントライブラリ-モジュール
TodoMVCは非常に小さくシンプルなアプリケーションです-そのインターフェースを記述するために必要なコンポーネントは4つだけであったため、ページ内でこのすべてを記述することができました。 実際には、多くのコンポーネントが必要になる場合があります。 画面、パネル、クッキーカッター、ポップアップ、その他すべてのことは言うまでもなく、多数のボタン、入力、その他のスライダーが発生します。これがUIが大好きな理由です。
もちろん、このすべてをページに記述するのは便利ではありません。別のファイルで選択する必要があります。これらは単なるファイルではなく、コンポーネントライブラリを持つモジュールであることが望ましいです。 W3Viewを使用すると、モジュールをロードし、それらの間の依存関係をその場で解決できます。 これを行うには、 moduleLoaderを使用し、次のように使用します。
<script src="../w3view.js"></script> <script src="moduleLoader.js"></script> <script src="httpreader.js"></script> <script> var appContext = {}; moduleLoader(appContext, '../examples/modules/window.w3v.html', reader, function(factory){ factory.create('app').mount(document.body); }); </script>
これは、 w3viewパッケージのloaderフォルダーからの例です。
moduleLoaderは4つの引数を取ります。
- appContext-アプリケーションコンテキスト、
- path-ルートモジュールへのパス、
- リーダー -HTTPリーダー(XHR)および
- コールバック -すべてがロードされたときにアプリケーションを起動する関数。 結果としてコンポーネントで満たされたW3Viewインスタンスを引数として受け取ります。
依存関係はIMPORTタグを使用して接続されます。別のライブラリに別のライブラリを接続する必要がある場合は、このライブラリに次のように記述する必要があります。
<import src="./realative/path/to/module" as="namespace" type="html"></import> <div as="my-component"> ......
as属性で指定された名前は、次のような名前空間としてさらに使用できます。
<import src="./realative/path/to/module" as="namespace" type="html"></import> <div as="my-component"> <namespace:another-component> </namespace:another-component> </div>
したがって、プラグインライブラリのすべてのコンポーネントが使用可能になります。 1つのモジュールを何度インポートしても、モジュールは1回だけ読み込まれ、再帰的な依存関係も許可されます。
ロード時間を短縮し、コンポーネントのHTML記述を解析するには、ビルダーを使用できます。 w3viewパッケージのbuilderフォルダーにあり、 build.jsファイルに使用方法が記載されています。 アセンブリの結果は、インポートされたすべてのライブラリを含む単一のjsファイルです。
最後に、読んでくれてありがとう
W3Viewについて知る必要があるのはこれだけです。 ライブラリ自体はこの説明よりもはるかに小さく、ソースを見るだけでその構造を理解することは難しくありません。できるだけシンプルで透過的にするようにしました。 ライブラリは規則を課していないため、問題を解決するのに適した方法でプログラムできます。
がんばって。