Maison > interface Web > js tutoriel > le corps du texte

À propos de l'interview : Écrivez une liaison de données bidirectionnelle pour Vue

不言
Libérer: 2020-09-01 16:40:21
original
7003 Les gens l'ont consulté

Le contenu de cet article concerne les interviews : l'écriture d'une liaison de données bidirectionnelle pour Vue, qui a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer

Dans l'interview frontale actuelle, Vue's. La liaison de données bidirectionnelle est devenue un point très facile à apprendre. Même si vous ne pouvez pas l'écrire sur place, vous devez au moins être capable d'en expliquer le principe. Dans cet article, j'écrirai un exemple de liaison de données bidirectionnelle basée sur vue. Appelons-le myVue. Combiné avec les commentaires, j'espère que tout le monde pourra y gagner quelque chose. (Recommandé : questions d'entretien avec Vue2020)

1 Principe

Je crois que tout le monde connaît le principe de la liaison de données bidirectionnelle de Vue, principalement via Object对象的defineProperty属性,重写data的set和get函数来实现的, je. Je ne décrirai pas trop le principe ici, je mettrai simplement en œuvre un exemple. Afin de rendre le code plus clair, seul le contenu le plus basique sera implémenté ici, implémentant principalement les trois commandes v-model, v-bind et v-click. D'autres commandes peuvent également être complétées par vous-même.

Ajouter une image depuis Internet

À propos de l'interview : Écrivez une liaison de données bidirectionnelle pour Vue

2. La mise en œuvre de la structure de la page

est très simple, comme suit

<p id="app">
    <form>
      <input type="text"  v-model="number">
      <button type="button" v-click="increment">增加</button>
    </form>
    <h3 v-bind="number"></h3>
  </p>
Copier après la connexion

Contient :

 1. 一个input,使用v-model指令
 2. 一个button,使用v-click指令
 3. 一个h3,使用v-bind指令。
Copier après la connexion

Nous allons enfin utiliser notre liaison de données bidirectionnelle d'une manière similaire à vue, et ajouter des commentaires combinés avec notre structure de données

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

Nous devons d'abord définir un constructeur myVue :

function myVue(options) {
  
}
Copier après la connexion

Afin d'initialiser ce constructeur, ajoutez-y un attribut _init

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(){}}
  }
Copier après la connexion

Ensuite, implémentez le Fonction _avers, car les données sont traitées, réécrivant les fonctions d'ensemble et d'obtention des données

et transformant la fonction _init

 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);
  }
Copier après la connexion

Ensuite, nous écrivons une classe d'instructions Watcher pour lier la fonction de mise à jour et implémenter la Mise à jour des éléments DOM

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内容进行了更新。
  }
Copier après la connexion

Mettre à jour la fonction _init et la fonction _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();
              })
            }
          }
        })
      }
    }
  }
Copier après la connexion

Alors comment lier la vue au modèle ? Ensuite, nous définissons une fonction _compile pour analyser nos instructions (v-bind, v-model, v-clickde), etc., et lions la vue et le modèle dans le processus.

 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;
        ))
      }
    }
  }
Copier après la connexion

Jusqu'à présent, nous avons implémenté une simple fonction de liaison bidirectionnelle de vue, comprenant trois instructions : v-bind, v-model et v-click. L'effet est comme indiqué ci-dessous

À propos de l'interview : Écrivez une liaison de données bidirectionnelle pour Vue

Ci-joint le code complet, moins de 150 lignes



  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>
Copier après la connexion

Recommandations associées :

Implémentation du code js de l'exemple de liaison de données bidirectionnelle Vue

Analyse du code source de liaison de données bidirectionnelle Vue

Principe de liaison de données bidirectionnelle Vue Explorer

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal