这篇文章我会仿照vue写一个双向绑定的实例,主要实v-model , v-bind , v-click
1、原理
Vue的双向数据绑定的原理大家可能或多或少了解一点,主要是通过 Object 对象的 defineProperty 属性,重写data的 set 和 get 函数来实现的。
2、页面结构
包含了
- 一个input,使用v-model指令
- 一个button,使用v-click指令
- 一个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 }
原文地址:https://www.cnblogs.com/gongyijie/p/9280662.html
时间: 2024-10-06 03:54:21