![原理](https://img-blog.csdnimg.cn/20200928152104534.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjcxNzkzNw==,size_16,color_FFFFFF,t_70#pic_center)
function define (obj, key, val) {
observable(val)
const dep = new Dep()
Object.defineProperty(obj, key, {
get() {
console.log('get', val);
Dep.target && dep.addDep(Dep.target)
return val
},
set(newval) {
if( newval != val) {
console.log('set', newval);
observable(val)
val = newval
dep.notfiy()
}
}
})
}
function observable(obj) {
if (typeof obj != 'object' || obj == null) {
return
}
new Observe(obj)
}
function proxy(vm, type) {
Object.keys(vm[type]).forEach(key => {
Object.defineProperty(vm, key ,{
get () {
return vm.$data[key]
},
set (v) {
vm.$data[key] = v
}
})
})
}
class Zvue {
constructor (options) {
this.$options = options
this.$data = options.data
this.$methods = options.methods
observable(this.$data)
proxy(this,'$data')
proxy(this,'$methods')
new Complie("#app", this)
}
}
class Observe {
constructor (value) {
this.value = value
this.walk(this.value)
}
walk(obj) {
Object.keys(obj).forEach(key => define(obj, key, obj[key]))
}
}
class Complie {
constructor(el, vm) {
this.$vm = vm
this.$el = document.querySelector(el)
if(this.$el) {
this.complie(this.$el)
}
}
complie(el) {
el.childNodes.forEach( node => {
if(this.isElement(node)) {
console.log('编译原属',node.nodeName);
this.complieElement(node)
} else if(this.isInter(node)) {
console.log('差值文本',node.textContent);
this.complieText(node)
}
if (node.childNodes) {
this.complie(node)
}
})
}
isElement(node) {
return node.nodeType === 1
}
isInter(node) {
return node.nodeType == 3 && /\{\{(.*)\}\}/.test(node.textContent)
}
complieText(node) {
this.update(node, RegExp.$1, 'text')
}
complieElement(node) {
console.log(node.attributes);
const attrs = node.attributes
Array.from(attrs).forEach( item => {
const names = item.name
const exp = item.value
if(this.isDrivet(item)) {
console.log(item.name.split('z-'));
const _attr = names.split('z-')[1]
this[_attr] && this[_attr](node, exp)
} else if(this.isMethdos(item)) {
const _attr = names.split('@')[1]
this[_attr] && this[_attr](node, exp, this)
}
})
}
textUpdater(node, val) {
node.textContent = val
}
text (node, exp) {
this.update(node, exp, 'text')
}
html(node,exp) {
this.update(node, exp, 'html')
}
htmlUpdater(node, val) {
node.innerHTML = val
}
model(node,exp) {
console.log(this);
var vm = this
node.addEventListener("input", (_attr,a) => {
vm.$vm[exp] = _attr.target.value
})
}
click(node, exp) {
console.log(this);
var vm = this
node.addEventListener("click", () => {
vm.$vm[exp]()
})
}
isDrivet(node) {
return node.name.indexOf('z-') == 0
}
isMethdos(node) {
return node.name.indexOf('@') == 0
}
update(node, exp, dir) {
const fn = this[dir+ 'Updater']
fn && fn(node, this.$vm[exp])
new watcher(this.$vm, exp, function(val) {
fn && fn(node, val)
})
}
}
class watcher {
constructor(vm, key, updatefn) {
this.$vm = vm
this.$key = key
this.$updatefn = updatefn
Dep.target = this
this.$vm[this.$key]
Dep.target = null
}
update() {
this.$updatefn.call(this.$vm, this.$vm[this.$key])
}
}
class Dep {
constructor() {
this.deps = []
}
addDep(watchers) {
this.deps.push(watchers)
}
notfiy () {
this.deps.forEach( watcher => watcher.update())
}
}