読書をお勧めします:
非常にシンプルなJS双方向データバインディングを実装します
MVVMは、Webフロントエンドで非常に人気のある開発モデルです。 MVVMを使用すると、コードがDOM操作を気にするのではなく、ビジネスロジックへの対処に重点を置くことができます。現在、有名なMVVMフレームワークにはVue、Avalon、Reactなどが含まれます。これらのフレームワークには独自の利点がありますが、実装のアイデアはほぼ同じです:データバインディング +ビューリフレッシュ。好奇心と闘争の意欲から、私はこの方向に沿って最も単純なMVVMライブラリ(MVVM.JS)を書き、合計2,000行以上のコードを書きました。指示の命名と使用法は、Vueに似ています。ここでは、実装の原則とコード組織のアイデアを共有します。
アイデアソート
MVVMは概念的には、ビューをデータロジックから真に分離するパターンであり、ViewModelはパターン全体の焦点です。 ViewModelを実装するには、データモデル(モデル)とビュー(ビュー)を関連付ける必要があります。実装のアイデア全体を5ポイントに単純に要約することができます。
要素の各ノードの指示をスキャンおよび抽出するコンパイラを実装します。
パーサーを実装して、要素の命令を解析します。これにより、リフレッシュ機能を介して命令の意図をDOMに更新できます(ビューリフレッシュの責任があるモジュールが中央で必要になる場合があります)。たとえば、ノード<p v-show = "isshow"> </p>を解析する場合、最初にモデルでISShowの値を取得し、次にNode.style.displayを変更して、要素の表示と非表示を制御します。
ウォッチャーの実装は、パーサーの各命令の更新関数をモデルに対応するフィールドにリンクできます。
オブジェクトのすべてのフィールドの値の変更を監視するためにオブザーバーを実装し、変更が発生したら最新の値を取得し、通知コールバックをトリガーできます。
オブザーバーを使用して、ウォッチャーのモデルの監視を確立します。モデルの値が変更されると、監視がトリガーされます。ウォッチャーが新しい値を取得した後、ステップ2に関連付けられた更新機能を呼び出します。これにより、データが変更されている間にビューを更新する目的を達成できます。
効果の例
まず、他のMVVMフレームワークのインスタンス化に似た最終的な使用例を見てみましょう。
<div id = "mobile-list"> <h1 v-text = "title"> </h1> <ul> <li v-for = "item in brands"> <b v-text = "item.name.name"> </b> <span v-show = "showrank"> lank:{{item.rank}}} </span> </li> </ul> </ul> ul> var> var empenia document.queryselector( '#mobile-list'); var vm = new mvvm(element、{'title'、 'mobile list'、 'showrank':true、 'brands':['name': 'apple'、 'rank':1}、{'name': 'galaxy'、 'rank':2}、{'' name '、' cupr '、' cupp 3}]}); vm.set( 'title'、 'トップ3モバイルランクリスト'); // => <h1>トップ3モバイルランクリスト</h1>モジュール部門
MVVMを5つのモジュールに分割して実装しました。コンパイルモジュールコンパイラ、パーサー、リフレッシュモジュールアップター、データサブスクリプションモジュールウォッチャー、およびデータリスニングモジュールオブザーバーの表示。このプロセスは、コンパイラがコマンドをコンパイルした後、命令情報が解析のためにパーサーに引き渡されると簡単に説明できます。パーサーは初期値を更新し、データの変更についてウォッチャーに購読します。オブザーバーはデータの変更を監視し、ウォッチャーに返します。ウォッチャーは、変更結果のアップデーターに通知し、対応する更新機能を見つけてビューを更新します。
上記のプロセスを図に示します。
以下は、これらの5つのモジュールの実装の基本原則の説明です(コードは重要な部分にのみ投稿されます。完全な実装のために私のgithubにアクセスしてください)
1.モジュールコンパイラをコンパイルします
コンパイラの責任は、主に要素の各ノードの命令をスキャンして抽出することです。コンピレーションと解析プロセスは、コンピレーション効率を改善するために、MVVMコンストラクターのコンパイル効率を改善するために、ノードツリー全体を何度も通過するため、最初に要素をドキュメントフラグメントのコピーに変換します。コンパイルオブジェクトはこのドキュメントフラグメントであり、ターゲット要素であってはなりません。すべてのノードがコンパイルされた後、ドキュメントフラグメントが元の実際のノードに追加されます。
VM.complieElementは、要素のすべてのノードのスキャンと命令抽出を実装します。
vm.compileElement(frucment、root){var node、childnodes = fragment.childnodes; //チャイルドノードの(var i = 0; i <churdnodes.length; i ++){node = childnodes [i]; if(this.hasdirective(node))子ノードの子ノードをスキャンif(node.childnodes.length){this.compileElement(node、false);}} //子ノードをスキャンif(root)if(root){this.compileallnodes();}}}}vm.compileallnodesメソッドは、これに各ノードをコンパイルします。ノードをコンパイルした後、キャッシュキューから削除されます。同時に、これを確認してください。$ uncompilenodes.length onghent === 0の場合、すべてのコンパイルが完了することを意味します。ドキュメントフラグメントを実際のノードに追加できます。
2。指示モジュールパーサー
コンパイラコンパイラが各ノードから命令を抽出すると、解析のためにパーサーに送信できます。各命令には異なる解析方法があります。すべての命令は2つのことを行う必要があります。1つはデータ値をビュー(初期状態)に更新することであり、もう1つはモデルの変更監視に更新機能をサブスクライブすることです。ここでは、ディレクティブの一般的な分析方法を説明するための例として、V-textの解析を使用します。
parser.parsevtext = function(node、model){//モデルvar text = this。$ model [model]; // text node.textcontent = text; // updater.updateNodetextContent(node、text); {node.textContent = last; // updater.updatenodetextContent(node、text);});};}3。データサブスクリプションモジュールウォッチャー
前の例では、Watcherはデータの変更を購読するWATHメソッドを提供します。 1つのパラメーターはモデルフィールドモデルで、もう1つはコールバック関数です。コールバック関数は、オブザーバーを介してトリガーされます。最後の新しい値と古い値は、パラメーターに渡されます。ウォッチャーが新しい値を取得した後、モデルの対応するコールバック(更新関数)を見つけるためにビューを更新できます。モデルと更新関数は1対多くの関係です。つまり、モデルには、v-text = "title"およびv-html = "title"命令がデータモデルフィールドを共有するなど、それを処理する多くのコールバック関数(更新関数)を持つことができます。
watcher.watchの実装方法にデータサブスクリプションを追加します。
watcher.watch = function(field、callback、context){var callbacks = this。$ watchcallbacks; if(!object.hasownproperty.call(this。$ model、field)){console.warn( 'field:' + field + 'はモデルに存在しない!'); [];} //キャッシュコールバック[field] .push([callback、context]);}データモデルのフィールドフィールドが変更されると、Watcherはフィールドにサブスクライブしたキャッシュアレイのすべてのコールバックをトリガーします。
4。データ監視モジュールオブザーバー
オブザーバーは、MVVM実装全体の中心的な基盤です。 OO(Object.Observe)がデータ結合革命に火をつけ、フロントエンドに大きな影響を与えるという記事を読みました。残念ながら、ES7ドラフトはOOを放棄しました!現在、ブラウザのサポートはありません!幸いなことに、オブジェクトプロパティのアクセス記述子(取得およびセット)を傍受することにより、単純なオブザーバーをシミュレートできるobject.definePropertyもあります。
// Intercept the get and set methods of the prop property of the object Object.defineProperty(object, prop, {get: function() {return this.getValue(object, prop);},set: function(newValue) {var oldValue = this.getValue(object, prop);if (newValue !== oldValue) {this.setValue(object, newValue, prop);// Trigger change callback this.triggerchange(prop、newValue、oldValue);}}});次に、別の質問があります:配列操作(プッシュ、シフトなど)を監視する方法は?すべてのMVVMフレームワークは、配列のプロトタイプを書き換えることにより実装されます。
Observer.RewritearRayMethods = function(array){var self = this; var arrayproto = array.prototype; var arraymethods = object.create(arrayproto); var method = 'push | shift | unshift | unshift | suplice |並べ替え|逆'.split(' | '); methods.foreach(function(method){object.defineProperty(arraymethods、method、function(){var i = arguments.length; var original = arrayproto [method]; var args = new array(i); self.triggerchange(this、method); return result;});}); array .__ proto__ = arraymethods;}この実装方法は、Vueから参照されます。非常によく使用されていると思いますが、配列の長さの属性を聞くことができないため、MVVMでは、Array.length操作を避ける必要があります。
5.更新モジュールアップターを表示します
Updaterは5つのモジュールの中で最も簡単です。各命令に対応する更新機能に責任を負う必要があります。たとえば、ビューやイベントの更新のために一連の投げて最終結果をアップデートに任せた後、V-textの更新関数は次のとおりです。
updater.updateNodetextContent = function(node、text){node.textcontent = text;}V-Bindの更新関数:スタイル:
updater.updatenodestyle = function(node、property、value){node.style [propperty] = value;}双方向データバインディングの実装
フォーム要素の双方向データ結合は、MVVMの最大の機能の1つです。
実際、この魔法の機能の実装の原則も非常に簡単です。すべきことは2つしかありません。1つは、データが変更されたときにフォーム値を更新することであり、もう1つはフォーム値が変更されたときにデータを更新して、データ値がフォーム値にバインドされることです。
フォーム値を更新するためのデータの変更は、上記のウォッチャーモジュールを使用して簡単に実行できます。
watcher.watch(model、function(last、old){input.value = last;}); 'フォームの変更でデータを更新するには、フォームの価値をリアルタイムで変更する価値のあるフォームを聞いて、データモデルの対応するフィールドを更新するだけです。
var model = this。$ model; input.addeventlistenr( 'change'、function(){model [field] = this.value;}); '他のフォームラジオ、チェックボックス、および選択は同じ原則です。
上記、各モジュールのプロセス全体と基本的な実装のアイデアが説明されています。コミュニティで初めて記事を投稿したとき、私の言語表現スキルはあまり良くありません。間違った言葉や悪いことが書かれている場合、誰もがそれらを批判し、修正できることを願っています!
結論
フレームワークプロジェクトでvue.jsを使用したため、この単純なMVVM.JSを試していますが、その命令システムを使用しました。多くの機能は、約4分の1のみを使用しました。データバインディングとビューリフレッシュを実装するだけで十分だと思いました。その結果、私はそのようなJavaScriptライブラリを見つけられなかったので、自分でそのようなホイールを作成しました。
機能と安定性は、Vueなどの一般的なMVVMフレームワークよりもはるかに少ないものであり、コードの実装は比較的荒い場合がありますが、このホイール〜進行状況を構築することで多くの知識が追加されています。
現在、私のMVVM.JSは最も基本的な機能を実装しています。私は将来それを改善し、強化し続けます。興味がある場合は、一緒に話し合い、改善してください〜