Heim > Web-Frontend > js-Tutorial > Vue implementiert eine bidirektionale Datenbindungsfunktion (mit Code)

Vue implementiert eine bidirektionale Datenbindungsfunktion (mit Code)

php中世界最好的语言
Freigeben: 2018-04-27 15:41:33
Original
2585 Leute haben es durchsucht

Dieses Mal werde ich Ihnen die bidirektionale Datenbindungsfunktion von Vue vorstellen (mit Code). Was sind die Vorsichtsmaßnahmen für Vue, um die bidirektionale Datenbindungsfunktion zu implementieren? Hier sind praktische Fälle, werfen wir einen Blick darauf.

1. Prinzip

Ich glaube, jeder kennt das Prinzip der bidirektionalen Datenbindung von Vue, hauptsächlich durch das Object Die defineProperty Das Attribut des Objekts wird durch Überschreiben der Set- und Get-Funktionen von Daten implementiert. Das Prinzip wird hier nicht zu ausführlich beschrieben, sondern dient hauptsächlich der Implementierung eines Beispiels. Um den Code klarer zu gestalten, werden hier nur die grundlegendsten Inhalte implementiert, hauptsächlich die Implementierung der drei Befehle V-Model, V-Bind und V-Click. Andere Befehle können auch von Ihnen selbst ergänzt werden.

Fügen Sie ein Bild aus dem Internet hinzu

2. Implementieren Sie die Seite

Struktur Sehr einfach, das folgende

<p id="app">
 <form>
  <input type="text" v-model="number">
  <button type="button" v-click="increment">增加</button>
 </form>
 <h3 v-bind="number"></h3>
 </p>
Nach dem Login kopieren

enthält:

1. Eine Eingabe mit dem V-Model-Befehl
2. Eine Schaltfläche mit dem V-Click-Befehl
3 . An h3, Verwenden Sie die v-bind-Direktive.

Endlich werden wir unsere bidirektionale Datenbindung auf ähnliche Weise wie Vue verwenden und Kommentare in Kombination mit unserer Datenstruktur hinzufügen

var app = new myVue({
  el:'#app',
  data: {
  number: 0
  },
  methods: {
  increment: function() {
   this.number ++;
  },
  }
 })
Nach dem Login kopieren

Zuerst müssen wir einen myVueKonstruktor:

function myVue(options) {
}
Nach dem Login kopieren
Um diesen Konstruktor zu initialisieren, fügen Sie ihm ein _init-Attribut hinzu

function myVue(options) {
 this._init(options);
}
myVue.prototype._init = function (options) {
 this.$options = options; // options 为上面使用时传入的结构体,包括el,data,methods
 this.$el = document.querySelector(options.el); // el是 #app, this.$el是id为app的Element元素
 this.$data = options.data; // this.$data = {number: 0}
 this.$methods = options.methods; // this.$methods = {increment: function(){}}
 }
Nach dem Login kopieren
Als nächstes implementieren Sie die Funktion _obverse, verarbeiten die Daten und schreiben die Set- und Get-Funktionen von Daten neu

Und transformieren Sie die _init-Funktion

myVue.prototype._obverse = function (obj) { // obj = {number: 0}
 var value;
 for (key in obj) { //遍历obj对象
  if (obj.hasOwnProperty(key)) {
  value = obj[key]; 
  if (typeof value === 'object') { //如果值还是对象,则遍历处理
   this._obverse(value);
  }
  Object.defineProperty(this.$data, key, { //关键
   enumerable: true,
   configurable: true,
   get: function () {
   console.log(`获取${value}`);
   return value;
   },
   set: function (newVal) {
   console.log(`更新${newVal}`);
   if (value !== newVal) {
    value = newVal;
   }
   }
  })
  }
 }
 }
 myVue.prototype._init = function (options) {
 this.$options = options;
 this.$el = document.querySelector(options.el);
 this.$data = options.data;
 this.$methods = options.methods;
 this._obverse(this.$data);
 }
Nach dem Login kopieren
Als nächstes schreiben wir einen Anweisungsklassen-Watcher, um die Update-Funktion an die Aktualisierung von DOM-Elementen zu binden

function Watcher(name, el, vm, exp, attr) {
 this.name = name;   //指令名称,例如文本节点,该值设为"text"
 this.el = el;    //指令对应的DOM元素
 this.vm = vm;    //指令所属myVue实例
 this.exp = exp;   //指令对应的值,本例如"number"
 this.attr = attr;   //绑定的属性值,本例为"innerHTML"
 this.update();
 }
 Watcher.prototype.update = function () {
 this.el[this.attr] = this.vm.$data[this.exp]; //比如 H3.innerHTML = this.data.number; 当number改变时,会触发这个update函数,保证对应的DOM内容进行了更新。
 }
Nach dem Login kopieren
Aktualisieren Sie die _init-Funktion und die _obverse-Funktion

myVue.prototype._init = function (options) {
 //...
 this._binding = {}; //_binding保存着model与view的映射关系,也就是我们前面定义的Watcher的实例。当model改变时,我们会触发其中的指令类更新,保证view也能实时更新
 //...
 }
 myVue.prototype._obverse = function (obj) {
 //...
  if (obj.hasOwnProperty(key)) {
  this._binding[key] = { // 按照前面的数据,_binding = {number: _directives: []}                                     
   _directives: []
  };
  //...
  var binding = this._binding[key];
  Object.defineProperty(this.$data, key, {
   //...
   set: function (newVal) {
   console.log(`更新${newVal}`);
   if (value !== newVal) {
    value = newVal;
    binding._directives.forEach(function (item) { // 当number改变时,触发_binding[number]._directives 中的绑定的Watcher类的更新
    item.update();
    })
   }
   }
  })
  }
 }
 }
Nach dem Login kopieren
Wie kann man also die Ansicht an das Modell binden? Als nächstes definieren wir eine _compile-Funktion, um unsere Anweisungen (v-bind, v-model, v-clickde) usw. zu analysieren und dabei die Ansicht und das Modell zu binden.

 myVue.prototype._init = function (options) {
 //...
 this._complie(this.$el);
 }
myVue.prototype._complie = function (root) { root 为 id为app的Element元素,也就是我们的根元素
 var _this = this;
 var nodes = root.children;
 for (var i = 0; i < nodes.length; i++) {
  var node = nodes[i];
  if (node.children.length) { // 对所有元素进行遍历,并进行处理
  this._complie(node);
  }
  if (node.hasAttribute(&#39;v-click&#39;)) { // 如果有v-click属性,我们监听它的onclick事件,触发increment事件,即number++
  node.onclick = (function () {
   var attrVal = nodes[i].getAttribute(&#39;v-click&#39;);
   return _this.$methods[attrVal].bind(_this.$data); //bind是使data的作用域与method函数的作用域保持一致
  })();
  }
  if (node.hasAttribute(&#39;v-model&#39;) && (node.tagName == &#39;INPUT&#39; || node.tagName == &#39;TEXTAREA&#39;)) { // 如果有v-model属性,并且元素是INPUT或者TEXTAREA,我们监听它的input事件
  node.addEventListener(&#39;input&#39;, (function(key) { 
   var attrVal = node.getAttribute(&#39;v-model&#39;);
   //_this._binding[&#39;number&#39;]._directives = [一个Watcher实例]
   // 其中Watcher.prototype.update = function () {
   // node[&#39;vaule&#39;] = _this.$data[&#39;number&#39;]; 这就将node的值保持与number一致
   // }
   _this._binding[attrVal]._directives.push(new Watcher( 
   &#39;input&#39;,
   node,
   _this,
   attrVal,
   &#39;value&#39;
   ))
   return function() {
   _this.$data[attrVal] = nodes[key].value; // 使number 的值与 node的value保持一致,已经实现了双向绑定
   }
  })(i));
  } 
  if (node.hasAttribute(&#39;v-bind&#39;)) { // 如果有v-bind属性,我们只要使node的值及时更新为data中number的值即可
  var attrVal = node.getAttribute(&#39;v-bind&#39;);
  _this._binding[attrVal]._directives.push(new Watcher(
   &#39;text&#39;,
   node,
   _this,
   attrVal,
   &#39;innerHTML&#39;
  ))
  }
 }
 }
Nach dem Login kopieren

Bisher haben wir eine einfache Zwei-Wege-Bindungsfunktion von Vue implementiert, einschließlich drei Anweisungen: V-Bind, V-Modell und V-Klick. Der Effekt ist wie unten gezeigt

Anbei ist der vollständige Code, weniger als 150 Zeilen



 myVue



 <p id="app">
 <form>
  <input type="text" v-model="number">
  <button type="button" v-click="increment">增加</button>
 </form>
 <h3 v-bind="number"></h3>
 </p>

<script>
 function myVue(options) {
 this._init(options);
 }
 myVue.prototype._init = function (options) {
 this.$options = options;
 this.$el = document.querySelector(options.el);
 this.$data = options.data;
 this.$methods = options.methods;
 this._binding = {};
 this._obverse(this.$data);
 this._complie(this.$el);
 }
 myVue.prototype._obverse = function (obj) {
 var value;
 for (key in obj) {
  if (obj.hasOwnProperty(key)) {
  this._binding[key] = {                                       
   _directives: []
  };
  value = obj[key];
  if (typeof value === 'object') {
   this._obverse(value);
  }
  var binding = this._binding[key];
  Object.defineProperty(this.$data, key, {
   enumerable: true,
   configurable: true,
   get: function () {
   console.log(`获取${value}`);
   return value;
   },
   set: function (newVal) {
   console.log(`更新${newVal}`);
   if (value !== newVal) {
    value = newVal;
    binding._directives.forEach(function (item) {
    item.update();
    })
   }
   }
  })
  }
 }
 }
 myVue.prototype._complie = function (root) {
 var _this = this;
 var nodes = root.children;
 for (var i = 0; i < nodes.length; i++) {
  var node = nodes[i];
  if (node.children.length) {
  this._complie(node);
  }
  if (node.hasAttribute(&#39;v-click&#39;)) {
  node.onclick = (function () {
   var attrVal = nodes[i].getAttribute(&#39;v-click&#39;);
   return _this.$methods[attrVal].bind(_this.$data);
  })();
  }
  if (node.hasAttribute(&#39;v-model&#39;) && (node.tagName == &#39;INPUT&#39; || node.tagName == &#39;TEXTAREA&#39;)) {
  node.addEventListener(&#39;input&#39;, (function(key) {
   var attrVal = node.getAttribute(&#39;v-model&#39;);
   _this._binding[attrVal]._directives.push(new Watcher(
   &#39;input&#39;,
   node,
   _this,
   attrVal,
   &#39;value&#39;
   ))
   return function() {
   _this.$data[attrVal] = nodes[key].value;
   }
  })(i));
  } 
  if (node.hasAttribute(&#39;v-bind&#39;)) {
  var attrVal = node.getAttribute(&#39;v-bind&#39;);
  _this._binding[attrVal]._directives.push(new Watcher(
   &#39;text&#39;,
   node,
   _this,
   attrVal,
   &#39;innerHTML&#39;
  ))
  }
 }
 }
 function Watcher(name, el, vm, exp, attr) {
 this.name = name;   //指令名称,例如文本节点,该值设为"text"
 this.el = el;    //指令对应的DOM元素
 this.vm = vm;    //指令所属myVue实例
 this.exp = exp;   //指令对应的值,本例如"number"
 this.attr = attr;   //绑定的属性值,本例为"innerHTML"
 this.update();
 }
 Watcher.prototype.update = function () {
 this.el[this.attr] = this.vm.$data[this.exp];
 }
 window.onload = function() {
 var app = new myVue({
  el:&#39;#app&#39;,
  data: {
  number: 0
  },
  methods: {
  increment: function() {
   this.number ++;
  },
  }
 })
 }
</script>
Nach dem Login kopieren
Ich glaube, dass Sie die Methode beherrschen, nachdem Sie den Fall gelesen haben Bitte achten Sie auf weitere spannende Artikel auf der chinesischen PHP-Website!

Empfohlene Lektüre:

jQuery$ und $().Detaillierte Erläuterung der Verwendung

Verwendung des .sync-Modifikators Ausführliche Erklärung

Das obige ist der detaillierte Inhalt vonVue implementiert eine bidirektionale Datenbindungsfunktion (mit Code). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage