Heim  >  Artikel  >  Web-Frontend  >  Über das Interview: Schreiben Sie eine bidirektionale Datenbindung für Vue

Über das Interview: Schreiben Sie eine bidirektionale Datenbindung für Vue

不言
不言Original
2018-04-10 16:42:146959Durchsuche

Der Inhalt dieses Artikels befasst sich mit Interviews: Schreiben einer bidirektionalen Datenbindung für Vue, die einen bestimmten Referenzwert hat. Freunde in Not können sich darauf beziehen

Im aktuellen Front-End-Interview, Vue Die bidirektionale Datenbindung ist zu einem sehr leicht zu erlernenden Punkt geworden. Auch wenn Sie es nicht sofort aufschreiben können, müssen Sie zumindest in der Lage sein, das Prinzip zu erklären. In diesem Artikel werde ich ein Beispiel für eine bidirektionale Datenbindung basierend auf Vue schreiben. Nennen wir es myVue. In Kombination mit den Kommentaren hoffe ich, dass jeder etwas gewinnen kann. (Empfohlen: Vue-Interviewfragen2020)

1. Prinzip

Ich glaube, jeder kennt das Prinzip der bidirektionalen Datenbindung von Vue, hauptsächlich durch Object对象的defineProperty属性,重写data的set和get函数来实现的, I Ich werde das Prinzip hier nicht zu sehr beschreiben, sondern nur ein Beispiel implementieren. Um den Code klarer zu gestalten, werden hier nur die grundlegendsten Inhalte implementiert, hauptsächlich drei Befehle: v-model, v-bind und v-click. Andere Befehle können auch von Ihnen selbst ergänzt werden.

Ein Bild aus dem Internet hinzufügen

Über das Interview: Schreiben Sie eine bidirektionale Datenbindung für Vue

2. Die Implementierung der

-Seitenstruktur ist sehr einfach folgt

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

Enthält:

 1. 一个input,使用v-model指令
 2. 一个button,使用v-click指令
 3. 一个h3,使用v-bind指令。

Wir werden endlich unsere bidirektionale Datenbindung ähnlich wie Vue verwenden und Kommentare in Kombination mit unserer Datenstruktur hinzufügen

var app = new myVue({
      el:&#39;#app&#39;,
      data: {
        number: 0
      },
      methods: {
        increment: function() {
          this.number ++;
        },
      }
    })

Zuerst müssen wir einen myVue-Konstruktor definieren:

function myVue(options) {
  
}

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(){}}
  }

Als nächstes implementieren Sie das _obverse Funktion zum Verarbeiten von Daten, Umschreiben der Set- und Get-Funktionen von Daten

und Transformieren der _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 === &#39;object&#39;) {  //如果值还是对象,则遍历处理
          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);
  }

Als nächstes schreiben wir einen Anweisungsklassen-Watcher, um die Aktualisierungsfunktion zu binden und zu implementieren Aktualisierung der DOM-Elemente

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内容进行了更新。
  }

Aktualisierung der Funktion _init und der Funktion _obverse

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();
              })
            }
          }
        })
      }
    }
  }

Wie bindet man also die Ansicht an das Modell? 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;
        ))
      }
    }
  }

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

Über das Interview: Schreiben Sie eine bidirektionale Datenbindung für Vue

Angehängt 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 === &#39;object&#39;) {
          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:&amp;#39;#app&amp;#39;,
      data: {
        number: 0
      },
      methods: {
        increment: function() {
          this.number ++;
        },
      }
    })
  }
</script>

Verwandte Empfehlungen:

JS-Code-Implementierung eines Beispiels für die bidirektionale Datenbindung von Vue

Analyse des Quellcodes für die bidirektionale Datenbindung von Vue

Vue-Prinzip der bidirektionalen Datenbindung Entdecken

Das obige ist der detaillierte Inhalt vonÜber das Interview: Schreiben Sie eine bidirektionale Datenbindung für Vue. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
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