熟悉vue的小夥伴應該熟悉vue中的v-model的使用方法,他的做用就是來實心雙數據綁定的,那麼先在來講明一下雙數據綁定的原理javascript
它的底層原理是由Object.defineProperty實現的vue
給一個對象添加或者修改屬性,返回一個對象java
參數一:目標對象數組
參數二:須要修改或添加的屬性app
參數三:給當前屬性的一些特徵this
接下來咱們來簡單實現如下prototype
var obj = {age:18} Object.defineProperty(obj,'name',{ value:'val' }) console.log(obj)
這樣咱們就實現了給對象添加name這個屬性值爲val對象
這樣咱們再次學他的其餘一些簡單的應用blog
var obj = {age:18} Object.defineProperty(obj,'name',{ value:'val', writable:false, //設置當前屬性是否能被修改 configurable:false, //設置當前屬性是否能被刪除 enumerable:false, //設置當前屬性是否能夠被枚舉 get(){}, //當屬性被獲取時觸發 set(){} //當屬性被修改時觸發 })
當使用set,get方法時候,不能使用value,writable這兩個屬性ip
原理:
咱們經過監聽一個數據的變化,再將數據變化時的值設置另外一個數據上
如今咱們依照原理來簡單實現如下
<body> <input type="text" id="txt"> <p id='msg'></p> <body>
<script> var obj = {massage:'隨便寫點'} var oTxt = document.getElementById('txt') var oMsg = document.getElementById('msg') //監聽input框的變化 oTxt.addEventListener('input',function(){ obj.massage = oTxt.value }) Object.defineProperty(obj,'massage',{ configurable:true, enumerable:true, set(newText){ //將監聽到的值賦給msg oMsg.innerText = newText } }) </script>
咱們這樣就完成了一個簡單的雙數據綁定
可是咱們尚未看見vue的影子,接下來咱們寫一個稍微複雜一點的雙數據綁定
<div id="app"> <input type="text" id="txt"> <p id="msg"></p> </div>
var oTxt = document.getElementById('txt') var oMsg = document.getElementById('msg') function Vue(options){ this.el = document.querySelector(options.el) this.data = options.data; this.init(this.data) } Vue.prototype = { constructor:Vue, init:function(obj){ Object.keys(obj).forEach((key)=>{ var value = obj[key] Object.defineProperty(obj,key,{ configurable:true, enumerable:true, set(newText){ if(value != newText){ console.log(newText) value = newText; oMsg.innerText = newText } } }) }) } } var vm = new Vue({ el:'#app', data:{ massage:'hello' } }) oTxt.addEventListener('input',function(){ vm.data.massage = this.value })
這樣看起來會比較像,可是仍是感受差了點什麼
因此咱們再寫一個最終版的vue雙數據綁定
<div id="app"> <input type="text" v-model="message"> <p>{{message}}</p> <p>{{message}}</p> <p>{{message}}</p> <p>{{message}}</p> <p>{{message}}</p> <p>{{message}}</p> <p>{{message}}</p> <input type="text" v-model="name"> <p>{{name}}</p> <p>{{name}}</p> <p>{{name}}</p> <p>{{name}}</p> </div>
function Vue(options){ this.el = document.querySelector(options.el); this.data = options.data; //是數據層和view之間的一個映射關係這裏面存放這個須要雙數據綁定的元素和當前元素的一些特徵 this.viewModel = {}; this.init(this.data); this.eventType(this.el); } Vue.prototype = { constructor:Vue, init:function(obj){ var _this = this; Object.keys(obj).forEach(function(key){ var value = obj[key]; //將當前key值做用的元素一些特徵保存在這個數組裏面 _this.viewModel[key] = { _directive:[] } console.log(_this.viewModel) Object.defineProperty(obj,key,{ configurable:true, enumerable:true, get:function(){ return value; }, set:function(newValue){ if(newValue!=value){ value = newValue; //數據更新 _this.viewModel[key]._directive.forEach(function(item){ item.update(); }) } } }) }) }, eventType:function(root){ var childs = root.children; console.log(childs) var _this = this; for(var i=0;i<childs.length;i++){ if(childs[i].hasAttribute("v-model") && childs[i].tagName == "INPUT"){ childs[i].addEventListener("input",(function(i){ var attr = childs[i].getAttribute("v-model"); _this.viewModel[attr]._directive.push(new watch( childs[i].tagName, "value", childs[i], _this, attr )) return function(){ //vm.data.message = _this.data[attr] = childs[i].value; } })(i)) } if(childs[i].innerText.replace(/\{\{|\}\}/g,"")){ var dataAttr = childs[i].innerText.replace(/\{\{|\}\}/g,""); _this.viewModel[dataAttr]._directive.push(new watch( childs[i].tagName, "innerText", childs[i], _this, dataAttr )) } } } } //更新數據的方法 標籤的名稱 當前元素 當前元素的屬性 vue的實例 data屬性 function watch(name,exp,el,vm,attr){ this.name = name; this.exp = exp; this.el = el; this.vm = vm; this.attr = attr; this.update(); } watch.prototype.update = function(){ this.el[this.exp] = this.vm.data[this.attr]; } var vm = new Vue({ el:"#app", data:{ message:"123", name:"456" } })