この記事では、主に javascript 双方向データ バインディングを実現する 3 つの方法の概要を紹介します。フロントエンド ビュー層 は、現在、双方向データ バインディングを実装する必要がある場合があります。興味のある方は双方向データバインディングを実装してください。
フロントエンドデータの双方向バインディング方法
フロントエンドのビュー層とデータ層は、mvvmフレームワーク、データなどの双方向バインディング(双方向バインディング)を実装する必要がある場合があります。 -ドリブン ビュー、ビュー ステート マシンなど、現在主流のいくつかの双方向データ バインディング フレームワークがまとめられています。現在、双方向データ バインディングを実装するには主に 3 つの方法があります。
1. 手動バインディング
古い実装メソッドは、オブザーバープログラミングモードに少し似ています。主なアイデアは、データオブジェクトに get メソッドと set メソッドを定義することです。呼び出し時に get または set data を手動で呼び出し、データを変更した後に UI レイヤーのレンダリング操作を開始します。ビューがデータ変更を駆動するシーンは、主に input、select、textarea などの要素に適用されます。 dom、keypress、keyup、その他のイベントの変化を監視して、データ層のデータを変更するイベントをトリガーすることで、UI 層が変化します。プロセス全体は関数呼び出しによって完了します。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>data-binding-method-set</title> </head> <body> <input q-value="value" type="text" id="input"> <p q-text="value" id="el"></p> <script> var elems = [document.getElementById('el'), document.getElementById('input')]; var data = { value: 'hello!' }; var command = { text: function(str){ this.innerHTML = str; }, value: function(str){ this.setAttribute('value', str); } }; var scan = function(){ /** * 扫描带指令的节点属性 */ for(var i = 0, len = elems.length; i < len; i++){ var elem = elems[i]; elem.command = []; for(var j = 0, len1 = elem.attributes.length; j < len1; j++){ var attr = elem.attributes[j]; if(attr.nodeName.indexOf('q-') >= 0){ /** * 调用属性指令,这里可以使用数据改变检测 */ command[attr.nodeName.slice(2)].call(elem, data[attr.nodeValue]); elem.command.push(attr.nodeName.slice(2)); } } } } /** * 设置数据后扫描 */ function mvSet(key, value){ data[key] = value; scan(); } /** * 数据绑定监听 */ elems[1].addEventListener('keyup', function(e){ mvSet('value', e.target.value); }, false); scan(); /** * 改变数据更新视图 */ setTimeout(function(){ mvSet('value', 'fuck'); },1000) </script> </body> </html>
2. ダーティ チェック メカニズム
典型的な mvvm フレームワークangularjs で代表される、angular はダーティ データをチェックすることで UI レイヤーの操作を更新します。 Angular のダーティ検出については、次のことを知っておく必要があります。 - ダーティ検出メカニズムは、スケジュールされた検出を使用しません。・ダーティ検出のタイミングはデータ変化時です。 - Angular は、一般的に使用される DOM イベント、XHR イベントなどをカプセル化し、Angular に入るダイジェスト プロセスをトリガーします。 - ダイジェスト プロセスでは、ルートスコープからトラバースし、すべてのウォッチャーをチェックします。 (angular の具体的な設計については、他のドキュメントを参照してください。ここではデータ バインディングについてのみ説明します) 次に、ダーティ検出を行う方法を見てみましょう。主にセット データを通じて、データに関連するすべての要素を見つけて比較します。データが変更される場合は、コマンド操作を実行します
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>data-binding-drity-check</title> </head> <body> <input q-event="value" ng-bind="value" type="text" id="input"> <p q-event="text" ng-bind="value" id="el"></p> <script> var elems = [document.getElementById('el'), document.getElementById('input')]; var data = { value: 'hello!' }; var command = { text: function(str) { this.innerHTML = str; }, value: function(str) { this.setAttribute('value', str); } }; var scan = function(elems) { /** * 扫描带指令的节点属性 */ for (var i = 0, len = elems.length; i < len; i++) { var elem = elems[i]; elem.command = {}; for (var j = 0, len1 = elem.attributes.length; j < len1; j++) { var attr = elem.attributes[j]; if (attr.nodeName.indexOf('q-event') >= 0) { /** * 调用属性指令 */ var dataKey = elem.getAttribute('ng-bind') || undefined; /** * 进行数据初始化 */ command[attr.nodeValue].call(elem, data[dataKey]); elem.command[attr.nodeValue] = data[dataKey]; } } } } /** * 脏循环检测 * @param {[type]} elems [description] * @return {[type]} [description] */ var digest = function(elems) { /** * 扫描带指令的节点属性 */ for (var i = 0, len = elems.length; i < len; i++) { var elem = elems[i]; for (var j = 0, len1 = elem.attributes.length; j < len1; j++) { var attr = elem.attributes[j]; if (attr.nodeName.indexOf('q-event') >= 0) { /** * 调用属性指令 */ var dataKey = elem.getAttribute('ng-bind') || undefined; /** * 进行脏数据检测,如果数据改变,则重新执行指令,否则跳过 */ if(elem.command[attr.nodeValue] !== data[dataKey]){ command[attr.nodeValue].call(elem, data[dataKey]); elem.command[attr.nodeValue] = data[dataKey]; } } } } } /** * 初始化数据 */ scan(elems); /** * 可以理解为做数据劫持监听 */ function $digest(value){ var list = document.querySelectorAll('[ng-bind='+ value + ']'); digest(list); } /** * 输入框数据绑定监听 */ if(document.addEventListener){ elems[1].addEventListener('keyup', function(e) { data.value = e.target.value; $digest(e.target.getAttribute('ng-bind')); }, false); }else{ elems[1].attachEvent('onkeyup', function(e) { data.value = e.target.value; $digest(e.target.getAttribute('ng-bind')); }, false); } setTimeout(function() { data.value = 'fuck'; /** * 这里问啥还要执行$digest这里关键的是需要手动调用$digest方法来启动脏检测 */ $digest('value'); }, 2000) </script> </body> </html>
3. フロントエンドデータハイジャック(ハイジャック)
3番目の方法は、avalonなどのフレームワークで使用されるデータハイジャック方法です。基本的な考え方は、Object.defineProperty を使用して、データ オブジェクトのプロパティの取得と設定を監視することです。このようにして、最も一般的な = 等号の割り当てを使用できます。 。具体的な実装は次のとおりです:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>data-binding-hijacking</title> </head> <body> <input q-value="value" type="text" id="input"> <p q-text="value" id="el"></p> <script> var elems = [document.getElementById('el'), document.getElementById('input')]; var data = { value: 'hello!' }; var command = { text: function(str) { this.innerHTML = str; }, value: function(str) { this.setAttribute('value', str); } }; var scan = function() { /** * 扫描带指令的节点属性 */ for (var i = 0, len = elems.length; i < len; i++) { var elem = elems[i]; elem.command = []; for (var j = 0, len1 = elem.attributes.length; j < len1; j++) { var attr = elem.attributes[j]; if (attr.nodeName.indexOf('q-') >= 0) { /** * 调用属性指令 */ command[attr.nodeName.slice(2)].call(elem, data[attr.nodeValue]); elem.command.push(attr.nodeName.slice(2)); } } } } var bValue; /** * 定义属性设置劫持 */ var defineGetAndSet = function(obj, propName) { try { Object.defineProperty(obj, propName, { get: function() { return bValue; }, set: function(newValue) { bValue = newValue; scan(); }, enumerable: true, configurable: true }); } catch (error) { console.log("browser not supported."); } } /** * 初始化数据 */ scan(); /** * 可以理解为做数据劫持监听 */ defineGetAndSet(data, 'value'); /** * 数据绑定监听 */ if(document.addEventListener){ elems[1].addEventListener('keyup', function(e) { data.value = e.target.value; }, false); }else{ elems[1].attachEvent('onkeyup', function(e) { data.value = e.target.value; }, false); } setTimeout(function() { data.value = 'fuck'; }, 2000) </script> </body> </html>
ただし、defineProperty は IE8 以降のブラウザをサポートしていることに注意してください。ただし、ブラウザの互換性のため、defineProperty を直接使用してください。 IE8 ブラウザに関しては、ハッキングするには他の方法を使用する必要があります。次のコードは IE8 をハッキングする可能性があり、defineProperty は IE8 をサポートします。たとえば、es5-shim.js を使用してください。 (IE8 未満のブラウザでは無視されます)
4. 概要
まず第一に、ここでの例は単純な実装にすぎませんが、読者は、この基本的な方法を通じて、複雑なフレームワークの類似点と相違点を深く感じることができます。アイデア。 。
以上がJavaScript で双方向データ バインディングを実装する 3 つのメソッドについて共有するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。