Vue 바인딩 문제 분석

php中世界最好的语言
풀어 주다: 2018-06-07 10:40:28
원래의
1417명이 탐색했습니다.

이번에는 Vue 바인딩 문제에 대한 분석과 Vue 바인딩 시 주의사항에 대해 알려드리겠습니다. 다음은 실제 사례입니다.

1. 원리

Vue의 양방향 데이터 바인딩 원리는 주로 Object 개체의 DefineProperty 속성을 통해 구현되며 여기에서 데이터의 함수를 가져옵니다. 원리를 너무 많이 설명하지는 않고 주로 예제를 구현해보겠습니다. 코드를 더 명확하게 하기 위해 여기서는 가장 기본적인 내용만 구현하며 주로 v-model, v-bind 및 v-click 세 가지 명령을 구현합니다. 다른 명령도 직접 보완할 수 있습니다.

인터넷에서 사진 추가

2.

페이지 구조 구현은 다음과 같이 매우 간단합니다.

<p id="app">
 <form>
  <input type="text" v-model="number">
  <button type="button" v-click="increment">增加</button>
 </form>
 <h3 v-bind="number"></h3>
 </p>
로그인 후 복사

에는 다음이 포함됩니다.

1. v-model 명령을 사용하는 입력
2. 버튼 , v-click 명령 사용
3. h3, v-bind 명령 사용.

마지막으로 vue와 유사한 방식으로 양방향 데이터 바인딩을 사용하여 데이터 구조와 결합된 주석을 추가합니다

var app = new myVue({
  el:'#app',
  data: {
  number: 0
  },
  methods: {
  increment: function() {
   this.number ++;
  },
  }
 })
로그인 후 복사

먼저 myVue 생성자를 정의해야 합니다.

function myVue(options) {
}
로그인 후 복사

이 생성자를 초기화하려면 다음을 제공합니다. _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(){}}
 }
로그인 후 복사

다음으로, _obverse 함수를 구현하고, 데이터를 처리하고, 데이터의 집합 및 가져오기 함수를

다시 작성하고 _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 === '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);
 }
로그인 후 복사

다음으로 바인딩할 Watcher 명령 클래스를 작성합니다. 업데이트 함수를 정의합니다. 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内容进行了更新。
 }
로그인 후 복사

_init 함수 및 _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();
    })
   }
   }
  })
  }
 }
 }
로그인 후 복사

그렇다면 뷰를 모델에 바인딩하는 방법은 무엇일까요? 다음으로, 지침(v-bind, v-model, v-clickde) 등을 구문 분석하고 프로세스에서 뷰와 모델을 바인딩하는 _compile 함수를 정의합니다.

 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;
  ))
  }
 }
 }
로그인 후 복사

지금까지 우리는 v-bind, v-model 및 v-click의 세 가지 명령을 포함하여 vue의 간단한 양방향 바인딩 기능을 구현했습니다. 효과는 아래와 같습니다

첨부된 코드는 150줄 미만입니다



 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>
로그인 후 복사

이 기사의 사례를 읽으신 후 방법을 마스터하셨다고 생각합니다. 더 흥미로운 정보를 보려면 다른 관련 항목에 주목하세요. PHP 중국어 웹사이트의 기사!

추천 자료:

JS를 사용하여 사용자의 도시와 지리적 위치를 얻는 방법

ES6 클래스를 사용하여 Vue를 모방하여 양방향 바인딩을 만듭니다

위 내용은 Vue 바인딩 문제 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿