页面代码:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Document</title>
<script src="./myvue.js"></script>
</head>
<body>
<div id="app">
{{name}}
<div>{{message}}</div>
<input v-model="test" type="text">
{{test}}
</div>
<script type="text/javascript">
let vm = new vue({
el: '#app',
data: {
name: 'lili',
message: 'test',
test: '双向绑定'
}
})
// console.log(vm);
// console.log(vm._data.name);
</script>
</body>
</html>
myvue.js 代码:
class vue extends EventTarget{ // 定义自定义事件,须要继承 EventTarget
constructor(option) {super() // 继承要执行父类的构造函数
this.option = option
this._data = this.option.data
this.el = document.querySelector(this.option.el)
this.observe(this._data)
this.compileNode(this.el)
}
observe(data) {
let self = this
this._data = new Proxy(data, {set(target, prop, newValue) {console.log(newValue);
let event = new CustomEvent(prop, { // 发送自定义事件, 实现双绑
detail: newValue
})
self.dispatchEvent(event)
return Reflect.set(...arguments)
}
})
}
compileNode(el) {
let child = el.childNodes // 伪数组
console.log(child);
[...child].forEach(node => { // 伪数开展为数组
if(node.nodeType === 3) { // 文本节点
console.log('这是一个文本节点');
let text = node.textContent
let reg = /\{\{\s*([^\s\{\}]+)\s*\}\}/g
if (reg.test(text)) {
let $1 = RegExp.$1 // 与正则表达式匹配的第一个 子匹配
this._data[$1] && (node.textContent = text.replace(reg, this._data[$1]))
this.addEventListener($1, e=> { // 接管自定义事件, 实现双绑
node.textContent = text.replace(reg, e.detail)
})
}
} else if (node.nodeType === 1) {console.log('这是一个元素节点');
let attr = node.attributes
if(attr.hasOwnProperty('v-model')) {let keyName = attr['v-model'].nodeValue
node.value = this._data[keyName]
node.addEventListener('input', e => {this._data[keyName] = node.value
})
}
this.compileNode(node)
}
})
}
}