class Vue {
constructor(option) {
// 1. 保存数据
this.$options = option
this.$data = option.data
this.$el = option.el
// 2. 将 data 中数据保存到响应式系统中
new Observer(this.$data)
// 3. 代理 this.$data 的数据
Object.keys(this.$data).forEach(key => {
this._proxy(key)
})
// 4. 处理 el
new Compiler(this.$el, this)
}
// 把 this.$data 的数据代理到 this 上
_proxy(key) {
Object.defineProperty(this, key, {
configurable: true,
enumerable: true,
get() {
return this.$data[key]
},
set(newVal) {
this.$data[key] = newVal
}
})
}
}
class Observer {
constructor(data) {
this.data = data
Object.keys(data).forEach(key => {
this.defineReactive(data, key, data[key])
})
}
defineReactive(data, key, val) {
const dep = new Dep(key)
Object.defineProperty(data, key, {
configurable: true,
enumerable: true,
get() {
if (Dep.target) {
dep.addSubs(Dep.target)
}
return val
},
set(newVal) {
if (val === newVal)
return
val = newVal
dep.notify()
}
})
}
}
const reg = /\{\{(.+)\}\}/
class Compiler {
constructor(el, vm) {
this.vm = vm
this.el = document.querySelector(el)
// 创建虚拟 DOM
const frag = this._createFragment(this.el)
// 把虚拟 DOM 挂载到页面上去
this.el.appendChild(frag)
}
_createFragment(node) {
const frag = document.createDocumentFragment()
let child
while (child = node.firstChild) {
console.log('push child', child);
if (child.firstChild) {
child = this._createFragment(child)
} else {
this._compiler(child)
}
frag.appendChild(child)
}
return frag
}
_compiler(node) {
if (node.nodeType === 1) {
// 标签节点
const attrs = node.attributes
console.log(attrs);
if (attrs.hasOwnProperty('v-model')) {
const name = attrs['v-model'].nodeValue
console.log(name);
node.value = this.vm[name]
node.addEventListener('input', e => {
let ev = e || event;
this.vm[name] = ev.target.value
})
new Watcher(node, name, this.vm)
}
} else if (node.nodeType === 3) {
// 文本节点
if (reg.test(node.nodeValue)) {
const name = RegExp.$1.trim()
console.log(node, name, this.vm);
new Watcher(node, name, this.vm)
}
}
}
}
class Dep {
constructor() {
this.subs = []
}
addSubs(watcher) {
this.subs.push(watcher)
}
notify() {
this.subs.forEach(sub => {
console.log('sub', sub);
sub.update()
})
}
}
class Watcher {
constructor(node, name, vm) {
this.vm = vm
this.node = node
this.name = name
Dep.target = this
this.update()
Dep.target = null
}
update() {
if (this.node.nodeType === 1) {
this.node.value = this.vm[this.name]
} else if (this.node.nodeType === 3) {
this.node.nodeValue = this.vm[this.name]
}
}
}