Vue 更新機制及資料繫結
阿新 • • 發佈:2018-12-17
一、更新機制。
參考帥氣的別人家博主:更新機制!
二、資料繫結
原理:
vue主要是藉助物件的訪問器屬性(Object.defineProperty)劫持資料,並結合訂閱者-釋出者模式來實現資料雙向繫結。
通過Object.defineProperty把data中的各資料屬性改為訪問器屬性,來劫持每個屬性的setter、getter;setter劫持到資料變化後,作為釋出者 釋出通知,訂閱者們接到通知後更新資料。
為了進一步說明vue原理,這裡我們藉助例項來進行講解。
例項:
有一段html程式碼(mvvm.html),需要保持 輸入框中資料和data中資料同步。需求有了,那我們如何實現呢?看程式碼:
mvvm.html程式碼:
<div id="app">
<input type="text" id="a" v-model='name' />
{{name}}
</div>
利用Object.defineProperty把data中的各資料屬性改為訪問器屬性:
//把data中的所有資料屬性修改為訪問器屬性: function observe(obj,vm){ Object.keys(obj).forEach(function(key){ defineReactive(obj,key,obj[key]); }) } //資料屬性修改為訪問器屬性: function defineReactive(obj,key,val){ var dep = new Dep();//主題物件,存放key屬性的所有訂閱者 Object.defineProperty(obj,key,{ get: function(){ if(Dep.target){ dep.subs.push(Dep.target);//新增訂閱者 } return val; }, set: function(newVal){ if(val==newVal)return; val = newVal;//資料劫持:當給key屬性賦值時,會觸發set方法 dep.notify();//釋出通知 } }) }
定義主題物件:新增訂閱者和釋出者方法
function Dep(){
this.subs = [];
}
Dep.prototype = {
addSub: function(sub){//新增訂閱者
this.subs.push(sub);
},
nodify: function(){//釋出通知
this.subs.forEach(function(sub){
sub.update();//訂閱者更新資料
})
}
}
訂閱者:新增資料更新方法
function Watcher(vm, node, name){ Dep.target = this; this.name = name; this.node = node; this.vm = vm; this.update(); Dep.target = null; } Watcher.prototype = { update: function(){//更新資料 this.get(); this.node.nodeValue = this.value; }, get: function(){//獲取資料 this.value = this.vm.data[this.name];//觸發get } }
利用documentFragment(文件片段)來處理節點,處理後再把文件片段插入掛載目標(注:操作documentFragment優於直接操作Dom節)
function nodeToFragment(node,vm){
var fragment = document.createDocumentFragment();
var child;
while(child = node.firstChild){
compile(child,vm);//編譯資料
fragment.appendChild(child);
}
return fragment;
}
編譯資料:解析模板指令,將模板中的變數替換成資料
function compile(node,vm){
var reg = /\{\{(.*)\}\}/;
if(node.nodeType==1){
var attrs = node.attributes;
for(var i=0,ln=attrs.length;i<ln;i++){
if(attrs[i].nodeName=='v-model'){
var name = attrs[i].nodeValue;
node.addEventListener("input",function(event){
//給相應的data賦值,觸發該屬性的set方法
vm.data[name]=event.target.value;
})
node.value = vm.data[name];//將data的值賦值給節點
node.removeAttribute('v-model');
}
}
}
if(node.nodeType==3){
if(reg.test(node.nodeValue)){
var name = (node.nodeValue).match(reg)[1];
name = name.trim();
new Watcher(vm, node, name);//為節點新增訂閱者方法
}
}
}
建立Vue構造方法:
function Vue(options){
this.data = options.data;
var data = this.data;
observe(data,this);
var id = options.el;
var dom = nodeToFragment(document.getElementById(id),this);
document.getElementById(id).appendChild(dom);
}
建立Vue示例:呼叫Vue,實現 輸入框資料和data資料同步:
var vm = new Vue({
el: 'app',
data: {
name:'Lucy'
}
})