class Vue {
constructor(el, data) {
this.el = document.querySelector(el)
this.data = data
this.domPool = {}
this.init()
}
init() {
// 数据劫持
this.Obersrve()
// 编译
this.Compile(this.el)
}
// 数据劫持 代理
Obersrve() {
const _this = this
// proxy代理
this.data = new Proxy(this.data, {
get(target, key) {
console.log(target, key, 'get');
return Reflect.get(target, key)
},
set(target, key, value) {
// 如果data改变就修改视图
_this.domPool[key].innerHTML = value
return Reflect.set(target, key, value)
}
})
}
// 编译 {{}} 以及input双向绑定
Compile(el) {
const childNodes = el.childNodes
// 遍历每一个节点
childNodes.forEach(node => {
// 如果是文本节点
if (node.nodeType === 3) {
const text = node.nodeValue
let reg = /\{\{(.*)\}\}/
if (text.trim().length) {
// 非空 且 符合格式
let _isValid = reg.test(text)
if (_isValid) {
const _key = text.match(reg)[1].trim()
this.domPool[_key] = node.parentNode
//通过reduce 方法 当取到类似{{ info.a }} 中间有. 的字符时,利用reduce的滚雪球特性 拿到最后的info.a的值
const value = _key.split('.').reduce((newObj, k) => newObj[k], this.data)
node.parentNode.innerText = value || undefined
}
}
}
// 如果是input框
if (node.nodeType === 1 && node.tagName === 'INPUT') {
console.log(node, 'input');
const _vModel = node.getAttribute('v-model')
if (_vModel) {
// 和data绑定 在input中显示data的数值
let value = _vModel.split('.').reduce((newObj, k) => newObj[k], this.data)
node.value = value
node.addEventListener('keyup', this.handleInput.bind(this, _vModel, node), false)
}
}
// 如果有子节点就继续递归
node.childNodes && this.Compile(node)
});
}
// input和data绑定
handleInput(key, input) {
const _value = input.value
this.data[key] = _value
console.log(key, _value);
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="app">
<div>
<input type="text" v-model="name" placeholder="姓名">
<input type="text" v-model="age" placeholder="年龄">
<input type="text" v-model="email" placeholder="邮箱">
<input type="text" v-model="tel" placeholder="电话">
<input type="text" v-model="info.a" placeholder="info.a">
</div>
<div>
<p>
</p>
<p> 姓名:<span>{{name}}</span></p>
<p> 年龄:<span>{{age}}</span></p>
<p> 邮箱:<span>{{email}}</span></p>
<p> info.a:<span>{{info.a}}</span></p>
<p>
<p>
电话: <span>{{tel}}</span>
</p>
</p>
</div>
</div>
</body>
</html>
<script src="./index.js"></script>
<script>
const vm = new Vue('#app', {
name: 'ruirui',
age: '1',
email: '@qq.com ',
tel: '22',
info: {
a: 1,
b: '123'
}
})
</script>