数据劫持结合订阅者观察者模式,通过节点的改变,将对应的收集对应的观察者,去触发对应的节点更新
let Global=null;
const utils = {
getValue(value,vm){
return vm.$data[value.trim()]
},
setValue(value,vm,newValue){
vm.$data[value] = newValue
},
model(node,value,vm){
const initValue = this.getValue(value,vm)
console.log(initValue)
new Watcher(value,vm,(newValue)=>{
this.modelUpdate(node,newValue)
})
node.addEventListener('input',(e)=>{
const newValue = e.target.value
this.setValue(value,vm,newValue)
})
this.modelUpdate(node,initValue)
},
modelUpdate(node,newVal){
node.value=newVal
},
text(node,value,vm){
// console.log(node,value,vm)
let result;
if(value.includes('{{')){
result = value.replace(/\{\{(.+?)\}\}/g,(...args)=>{
const expr = args[1]
new Watcher(expr,vm,(newVal)=>{
this.textUpdate(node,newVal)
})
return this.getValue(args[1],vm)
})
}else{
result = this.getValue(value,vm)
}
this.textUpdate(node,result)
},
on(node,value,vm,event){
console.log(vm,'vm')
const fn = vm.options.methods[value]
node.addEventListener(event,fn.bind(vm),false)
},
textUpdate(node,newValue){
node.textContent = newValue
}
}
// 收集dom的依赖
class Watcher{
constructor(expr,vm,cb){
this.expr = expr;
this.vm = vm;
this.cb = cb;
// 通过getter对数据进行绑定,标记当前的watcher
this.oldValue = this.getOldValue();
}
getOldValue(){
Global = this
const oldValue = utils.getValue(this.expr,this.vm)
Global = null
return oldValue
}
update(){
const newValue = utils.getValue(this.expr,this.vm)
if(newValue!==this.oldValue){
this.cb(newValue)
}
}
}
// 把我们的数据跟多个Watcher绑定起来
class Dep{
constructor(){
this.collect = []
}
addwatcher(watcher){
this.collect.push(watcher)
}
notify(){
this.collect.forEach(w=>w.update())
}
}
class Observe{
constructor(data){
this.observe(data)
}
observe(data){
if(typeof data ==='object'){
Object.keys(data).forEach(key=>{
this.defineReactive(data,key,data[key])
})
}
}
defineReactive(obj,key,value){
this.observe(value)
const dep = new Dep()
Object.defineProperty(obj,key,{
get(){
const target = Global
Global && dep.addwatcher(target)
// console.log('get',key,value)
return value
},
set:(newVal)=>{
console.log('set',newVal)
if(newVal===value) return;
this.observe(newVal)
value = newVal
dep.notify()
}
})
}
}
class Compiler{
constructor(el,vm){
this.el = this.isElementNode(el) ? el:document.querySelector(el)
this.vm = vm
const fragment = this.compileFragment(this.el)
this.compiler(fragment)
this.el.appendChild(fragment)
}
compiler(fragment){
const childNodes = Array.from(fragment.childNodes)
// console.log(childNodes)
childNodes.forEach(childNode=>{
if(this.isElementNode(childNode)){
// console.log('标签',childNode)
this.compilerElement(childNode)
}else if(this.isTextNode(childNode)){
this.compileText(childNode)
// console.log('文本',childNode)
}
if(childNode.childNodes&&childNode.childNodes.length){
this.compiler(childNode)
}
})
}
// 解析文本节点
compileText(node){
const text = node.textContent
if(/\{\{(.+)\}\}/.test(text)){
utils['text'](node,text,this.vm)
console.log('text',text)
}
}
// 解析标签节点
compilerElement(node){
const attributes = Array.from(node.attributes)
attributes.forEach(attr=>{
const { name,value } = attr
// console.log('attr',name,value)
if(this.isDirector(name)){
// 指令 v-for ;v-on:click ;v-bind ;v-if ;v-model
const [ , directorName ] = name.split('-')
const [ compileKey , eventName ] = directorName.split(':')
// console.log(directorName,compileKey,eventName,'123')
utils[compileKey](node,value,this.vm,eventName)
}else if(this.isEvent(name)){
const [,eventName] = name.split('@')
utils['on'](node,value,this.vm,eventName)
}
})
}
isDirector(name){
return name.startsWith('v-')
}
isEvent(name){
return name.startsWith('@')
}
isTextNode(el){
return el.nodeType === 3
}
// 创建文档节点
compileFragment(el){
const f = document.createDocumentFragment(el)
// console.log(f,'f')
let firstChild;
// console.log(el,el.firstChild,'el')
while(firstChild=el.firstChild){
f.appendChild(firstChild)
}
return f;
// console.log(f,'f')
}
isElementNode(el){
return el.nodeType===1
}
}
// Vue类
class Vue{
constructor(options){
this.$el = options.el;
this.$data = options.data;
this.options = options
// 将data绑定到vue实例上面,并触发this.$data.xxx与模板的绑定
new Observe(this.$data)
// 解析模板
new Compiler(this.$el,this)
this.ProxyData(this.$data)
}
ProxyData(data){
Object.keys(data).forEach(key=>{
Object.defineProperty(this,key,{
get(){
console.log('获取:key',key,data[key])
return data[key]
},
set(newVal){
data[key] = newVal
}
})
})
}
}