1. 程式人生 > >vue的雙向數據綁定

vue的雙向數據綁定

解析 ali clas onf attribute 指令 實例 fig name

  這篇文章我會仿照vue寫一個雙向綁定的實例,主要實v-model , v-bind , v-click

1、原理

  Vue的雙向數據綁定的原理大家可能或多或少了解一點,主要是通過 Object 對象的 defineProperty 屬性,重寫data的 set get 函數來實現的。

  技術分享圖片

2、頁面結構

  技術分享圖片

包含了 

  1. 一個input,使用v-model指令

  2. 一個button,使用v-click指令

  3. 一個h3,使用v-bind指令。

我們最後會通過類似於vue的方式來使用我們的雙向數據綁定,結合我們的數據結構添加註釋:

技術分享圖片

首先我們需要定義一個myVue構造函數:

技術分享圖片

為了初始化這個構造函數,給它添加一個 _init 屬性:

技術分享圖片

接下來實現 _obverse 函數,對data進行處理,重寫data的set和get函數:

並改造_init函數

技術分享圖片

接下來我們寫一個指令類Watcher,用來綁定更新函數,實現對DOM元素的更新。

技術分享圖片

更新 _init 函數以及 \_obverse 函數:

技術分享圖片

那麽如何將view與model進行綁定呢?接下來我們定義一個 _compile 函數,用來解析我們的指令(v-bind,v-model,v-clickde)等,並在這個過程中對view與model進行綁定。

技術分享圖片

至此,我們已經實現了一個簡單vue的雙向綁定功能,包括v-bind, v-model, v-click三個指令。效果如下圖:

  技術分享圖片

附上全部代碼

  1 <!DOCTYPE html>
  2 
  3 <head>
  4 
  5  <title>myVue</title>
  6 
  7 </head>
  8 
  9 <style>
 10 
 11  #app {
 12 
 13    text-align: center;
 14 
 15  }
 16 
 17 </style>
 18 
 19 <body>
 20 
 21  <div id="app">
 22 
 23    <
form> 24 25 <input type="text" v-model="number"> 26 27 <button type="button" v-click="increment">增加</button> 28 29 </form> 30 31 <h3 v-bind="number"></h3> 32 33 </div> 34 35 </body> 36 37 <script> 38 39 function myVue(options) { 40 41 this._init(options); 42 43 } 44 45 myVue.prototype._init = function (options) { 46 47 this.$options = options; 48 49 this.$el = document.querySelector(options.el); 50 51 this.$data = options.data; 52 53 this.$methods = options.methods; 54 55 this._binding = {}; 56 57 this._obverse(this.$data); 58 59 this._complie(this.$el); 60 61 } 62 63 myVue.prototype._obverse = function (obj) { 64 65 var value; 66 67 for (key in obj) { 68 69 if (obj.hasOwnProperty(key)) { 70 71 this._binding[key] = { 72 73 _directives: [] 74 75 }; 76 77 value = obj[key]; 78 79 if (typeof value === object) { 80 81 this._obverse(value); 82 83 } 84 85 var binding = this._binding[key]; 86 87 Object.defineProperty(this.$data, key, { 88 89 enumerable: true, 90 91 configurable: true, 92 93 get: function () { 94 95 console.log(`獲取${value}`); 96 97 return value; 98 99 }, 100 101 set: function (newVal) { 102 103 console.log(`更新${newVal}`); 104 105 if (value !== newVal) { 106 107 value = newVal; 108 109 binding._directives.forEach(function (item) { 110 111 item.update(); 112 113 }) 114 115 } 116 117 } 118 119 }) 120 121 } 122 123 } 124 125 } 126 127 myVue.prototype._complie = function (root) { 128 129 var _this = this; 130 131 var nodes = root.children; 132 133 for (var i = 0; i < nodes.length; i++) { 134 135 var node = nodes[i]; 136 137 if (node.children.length) { 138 139 this._complie(node); 140 141 } 142 143 if (node.hasAttribute(v-click)) { 144 145 node.onclick = (function () { 146 147 var attrVal = nodes[i].getAttribute(v-click); 148 149 return _this.$methods[attrVal].bind(_this.$data); 150 151 })(); 152 153 } 154 155 if (node.hasAttribute(v-model) && (node.tagName == INPUT || node.tagName == TEXTAREA)) { 156 157 node.addEventListener(input, (function(key) { 158 159 var attrVal = node.getAttribute(v-model); 160 161 _this._binding[attrVal]._directives.push(new Watcher( 162 163 input, 164 165 node, 166 167 _this, 168 169 attrVal, 170 171 value 172 173 )) 174 175 return function() { 176 177 _this.$data[attrVal] = nodes[key].value; 178 179 } 180 181 })(i)); 182 183 } 184 185 if (node.hasAttribute(v-bind)) { 186 187 var attrVal = node.getAttribute(v-bind); 188 189 _this._binding[attrVal]._directives.push(new Watcher( 190 191 text, 192 193 node, 194 195 _this, 196 197 attrVal, 198 199 innerHTML 200 201 )) 202 203 } 204 205 } 206 207 } 208 209 function Watcher(name, el, vm, exp, attr) { 210 211 this.name = name; //指令名稱,例如文本節點,該值設為"text" 212 213 this.el = el; //指令對應的DOM元素 214 215 this.vm = vm; //指令所屬myVue實例 216 217 this.exp = exp; //指令對應的值,本例如"number" 218 219 this.attr = attr; //綁定的屬性值,本例為"innerHTML" 220 221 this.update(); 222 223 }

vue的雙向數據綁定