vue.js雙向資料繫結實現原理
阿新 • • 發佈:2019-02-04
/*var dom = nodeToFragment(document.getElementById('app'));
console.log(dom);*/
function compile(node,vm) {
var reg =/\{\{(.*)\}\}/;
//節點型別為元素
if(node.nodeType === 1){
var attr = node.attributes;
//解析屬性
for(var i=0;i<attr.length;i++){
if (attr[i].nodeName = 'v-model'){
var name = attr[i].nodeValue;//獲取v-model繫結的屬性名
//放在該位置原因是獲取name值
node.addEventListener('input',function (e) {
//給相應的data屬性賦值,進而觸發該屬性的set方法
vm[name]=e.target.value;
});
node.value = vm[name];
node.removeAttribute('v-model' );
}
};
}
//節點型別為text
if(node.nodeType === 3){
if(reg.test(node.nodeValue)){
var name = RegExp.$1;//1.獲取匹配到的字串
name = name.trim();
//node.nodeValue = vm[name];//將data的值賦值給該node
new Watcher(vm,node,name);
}
}
}
function nodeToFragment(node,vm) {
var flag = document.createDocumentFragment();
var child;
while(child = node.firstChild){
compile(child,vm);
flag.append(child);
}
return flag;
}
function defineReactive(obj,key,val) {
var dep = new Dep();
Object.defineProperty(obj,key,{
get:function () {
if(Dep.target) dep.addSub(Dep.target);
return val;
},
set:function (newVal) {
if(newVal===val) return ;
val = newVal;
//console.log(val);
//作為釋出者發出通知
dep.notify();
}
});
}
function observe(obj,vm) {
Object.keys(obj).forEach(function (key) {
defineReactive(vm,key,obj[key]);
})
}
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);
//編譯完成後,將dom返回到app中
document.getElementById('app').appendChild(dom);
}
function Dep() {
this.subs = [];
}
Dep.prototype={
addSub:function (sub) {
this.subs.push(sub);
},
notify: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;
},
//獲取data中的屬性值
get:function () {
this.value = this.vm[this.name];//觸發相應屬性的get
}
};
var vm = new Vue({
el:'app',
data:{
text:'hello world'
}
});