#html部分
<div id="app">
{{name}}<input id="inputText" type="text" v-model="name">{{age}}<input id="inputText" type="text" v-model="age">
</div>
#js部分
var n = 0;
function Vue(opt){
this.data = opt.data;
let _id = document.querySelector(opt.el);
//observe需要在compile之前这样,不然初始化赋值取不到值
observe(this.data,this);
let _dom = cloneNode(_id,this);
_id.appendChild(_dom);
}
function observe(obj,vm){
Object.keys(obj).forEach(key => {
defineReactive(vm,key,obj[key])
})
}
// 观察者
function Watcher(vm,node,name,nodeType){
console.log('新')
this.vm = vm;
this.node = node;
this.name = name;
this.nodeType = nodeType;
Dep.target = this;
console.log(Dep.target);
}
Watcher.prototype = {
get:function(){
this.value = this.vm[this.name]
},
updata:function(){
this.get()
if(this.nodeType == 'input'){
this.node.value = this.value
}
if(this.nodeType == 'text'){
this.node.nodeValue = this.value
}
}
}
//订阅者构造函数
function Dep(){
this.subs = [] //存放我们观察者的一个数组
}
Dep.prototype = {
notify:function(){
console.log(this.subs);
this.subs.forEach(sub =>{
sub.updata()
})
},
addSub:function(sub){
console.log('没执行');
this.subs.push(sub);
//这里需要对sub,即watcher去重,可能实现的不是特别好
this.subs = [...new Set(this.subs)]
console.log(this.subs);
}
}
function defineReactive(obj,key,val){
//订阅者
console.log(++n);
var x = n;
let dep = new Dep();
Object.defineProperty(obj,key,{
get:function(){
//Dep.target 观察者实例 挂载到Dep
if(Dep.target){
console.log("Dep.target")
console.log(Dep.target);
dep.addSub(Dep.target)
}
return val
},
set:function(newValue){
if(val == newValue){
return
}
val = newValue
console.log(`设置${newValue}`)
dep.notify();
}
})
}
function compile(node,vm){
console.log('compile');
console.dir(node.nodeType)
var name =''
let reg = /\{\{(.*)\}\}/;
let nodeType = 'input'
if(node.nodeType == 1){
let attr = node.attributes
for(let i = 0,al=attr.length;i<al;i++){
if(attr[i].nodeName == 'v-model'){
nodeType = 'input'
name = attr[i].nodeValue;
/*new Watcher放在获取值getter之前,new Watcher 会直接Dep.target赋值,
后面getter执行时,addSub进dep.subs(Dep实例),watcher不会少*/
new Watcher(vm,node,name,nodeType)
node.addEventListener('input',function(){
vm[name] = this.value;
},false)
node.value = vm[name]
node.removeAttribute('v-model')
}
}
}
if(node.nodeType == 3){
// console.log(node.childNodes[0].nodeValue)
if(reg.test(node.nodeValue)){
nodeType = 'text'
name = RegExp.$1;
new Watcher(vm,node,name,nodeType)
node.textContent = vm[name];
}
}
}
function cloneNode(node,vm){
let flag = document.createDocumentFragment()
let child
while(child = node.firstChild) {
compile(child,vm)
flag.appendChild(child)
}
return flag
}
var vm = new Vue({
data:{name:'aaa',age:'30'},
el:'#app'
})
//总结:
1.new Vue,首先进行el挂载,data绑定到observer上,node节点拦截,重新生成节点,
每个符合{{}}和v-model的节点,都会生成一个watcher,watcher构造函数里面,将this赋值给Dep.target
即是当前的Watcher实例,并且如果节点是input监听input输入,下一步,执行获取属性,Observer里面
的getter,这时Dep.target是之前刚赋值的Watcher实例,把它添加到这个属性的dep的subs数组中,
然后取值。
2.Observer是数据的监听,每个属性都有一个dep收集器,dep收集器里的subs存放着该属性的watcher,
当数据更新时,会触发Observer的setter会调用dep.notifys,执行该dep的subs的每个sub的updata方法,
根据不同的node结构,进行赋值