class Vue {
constructor(options) {
this.$data = options.data
Observe(this.$data)
ProxyData(this.$data, this)
Compile(this, options.el)
}
}
function Observe(data) {
if (data && typeof data === 'object') {
const dep = new Dep()
Object.keys(data).forEach(key => {
let value = data[key]
Observe(value)
Object.defineProperty(data, key, {
get() {
console.log(`获取了${key}的值`)
Dep.target && dep.addSub(Dep.target)
return value
},
set(newval) {
console.log(`修改${key}的值`)
value = newval
Observe(value)
dep.notify()
}
})
})
}
}
function ProxyData(data, vm) {
if (data && typeof data === 'object') {
Object.keys(data).forEach(key => {
Object.defineProperty(vm, key, {
get() {
return data[key]
},
set(newval) {
data[key] = newval
}
})
})
}
}
function getValue(path, data) {
return path.split('.').reduce((newObj, key) => newObj[key], data)
}
function Compile(vm, el) {
vm.$el = document.querySelector(el)
const fragment = document.createDocumentFragment()
while (childNode = vm.$el.firstChild) {
fragment.appendChild(childNode)
}
replace(fragment)
vm.$el.appendChild(fragment)
function replace(node) {
const reg = /\{\{\s*(\S+?)\s*\}\}/
if (node.nodeType === 1 && node.nodeName.toUpperCase() === "INPUT") {
let temp = [...node.attributes]
let result = temp.find(item => item.name === 'v-module')
if (result) {
let value = getValue(result.nodeValue, vm)
node.value = value
new Watcher(vm, result.nodeValue, (newval) => {
node.value = newval
})
node.addEventListener('input', (e) => {
let arr = result.nodeValue.split('.')
let obj = arr.slice(0, arr.length - 1).reduce((newObj, key) => newObj[key], vm)
obj[arr[arr.length - 1]] = e.target.value
})
}
}
if (node.nodeType === 3) {
let text = node.nodeValue
let path = reg.exec(text)
if (path) {
let value = getValue(path[1], vm)
node.nodeValue = value
new Watcher(vm, path[1], (newval) => {
node.nodeValue = newval
})
}
}
node.childNodes.forEach(node => {
replace(node)
})
}
}
class Dep {
constructor() {
this.subs = []
}
addSub(watcher) {
this.subs.push(watcher)
}
notify() {
this.subs.forEach(watcher => watcher.update())
}
}
class Watcher {
constructor(vm, path, callback) {
this.vm = vm
this.path = path
this.callback = callback
Dep.target = this
getValue(path, vm)
Dep.target = null
}
update() {
let value = getValue(this.path, this.vm)
this.callback(value)
}
}