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

手寫vue雙向綁定數據

class 技術分享 bin rabl hasattr -c on() amp child

來一張原理圖:

技術分享圖片

實現思路:

  (1)綁定data 種的數據,為每個數據添加指令。通過Object,defineProperty() 來通知屬性是否更改

  (2) 找到每個DOM節點的指令。綁定事件。並綁定watcher

  (3) 實現DOM事件改變之後, 響應data數據,實現視圖更新

<!DocType>
<html>
  <title>vue 的雙向綁定事件</title>
  <body id="app">
    <input type="text" v-model="number"/>
    <span v-bind="
number"></span> <input type="text" v-model="age"/> <span v-bind="age"></span> </body> <script> function Vue (options) { this._init(options); } Vue.prototype._init = function (options) { this.$data = options.data; this
.$methods = options.data.methods; this.$el = document.querySelector(options.el); this.$methods = options.methods; this.$key = ‘‘; this._binding = {}; // 觀測數據 this._observer(this.$data); this._complie(this.$el); // this._test(this.$data); }
// 觀測數據 Vue.prototype._observer = function (obj) { var value; let _this = this; for (key in obj) { if (obj.hasOwnProperty(key)) { this._binding[key] = { _directives: [] }; value = obj[key]; if (typeof value === object) { this._observer(value); } Object.defineProperty(this.$data, key, { enumerable: true, configurable: true, get: function () { console.log(`獲取${value}`, key); return value; }, set: function (newVal) { console.log(key:, key, _this.$key); if (value !== newVal) { value = newVal; _this._binding[_this.$key]._directives.forEach(function (item, index) { item.update(); }) } } }) } } } // 為DOM節點添加指令事件 Vue.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(v-click)) { node.onclick = (function () { var attrVal = nodes[i].getAttribute(v-click); return _this.$methods[attrVal].bind(_this.$data); })(); } if (node.hasAttribute(v-model) && (node.tagName == INPUT || node.tagName == TEXTAREA)) { node.addEventListener(input, (function(key) { var attrVal = node.getAttribute(v-model); _this._binding[attrVal]._directives.push(new Watcher( input, node, _this, attrVal, value )) return function() { _this.$key = attrVal _this.$data[attrVal] = nodes[key].value; } })(i)); } if (node.hasAttribute(v-bind)) { var attrVal = node.getAttribute(v-bind); _this._binding[attrVal]._directives.push(new Watcher( text, node, _this, attrVal, innerHTML )) } } } 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]; } // 測試 Vue.prototype._test = function($data) { var a = $data.number; $data.number = 32; } window.onload = function () { var app = new Vue({ el: #app, data: { number: 12, age: 444 }, methods: {} }) } </script> </html>

手寫vue雙向綁定數據