<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="电话号码">
</div>
<div>
<p>姓名:<span>{{name}}</span></p>
<p>年龄:<span>{{age}}</span></p>
<p>邮箱:<span>{{email}}</span></p>
<p>
<p>电话:<span>{{tel}}</span></p>
</p>
</div>
</div>
class MVVM {
constructor(el, data) {
this.el = document.querySelector(el)
this.data = data
this.domPool = {} // dom 池,用来保存 el 下所有 dom 节点的父节点
this.init() // 初始化
}
init() {
this.initData() // 初始化数据
this.initDom() // 初始化界面
}
initData() {
const that = this
that._data = {} // 定义代理(劫持)的数据对象
for (let property in that.data) { // 对象代理
(function (property) {
Object.defineProperty(that._data, property, {
set(value) {
that.data[property] = value
that.domPool[property].innerText = value
},
get() {
return that.data[property]
}
})
})(property)
}
}
initDom() {
this.bindDom(this.el) // 解析 dom,把 {{ ... }} 中的属性解析成数据
this.bindInput(this.el) // 遍历 input,给 input 输入框绑定 keyup 响应事件
}
bindDom(el) {
const childNodes = el.childNodes // el 的子节点
childNodes.forEach(node => { // 遍历 el 的子节点
if (node.nodeType === 3) { // 如果节点类型是 3,即文本,因为 {{...}} 属于文本节点
const value = node.nodeValue // 节点的值
if (value.trim().length) {
const patten = /\{\{(.+?)\}\}/ // 匹配 {{}}
let isValid = patten.test(value) // 匹配成功
if (isValid) {
const property = value.match(patten)[1].trim() // {{}} 中的数据属性
this.domPool[property] = node.parentNode // 把父节点放入 dom 池
this.domPool[property].innerText = this._data[property] || undefined // 更改父节点的文本内容为代理对象对应属性的值
}
}
}
node.childNodes && this.bindDom(node) // 如果子节点还有子节点,继续递归遍历其子节点
})
}
bindInput(el) {
const allInputs = el.querySelectorAll('input')
allInputs.forEach(input => { // 遍历 input 输入框
const vModel = input.getAttribute('v-model') // input 的 v-model 属性
if (vModel) {
input.addEventListener('keyup', this.handleInput.bind(this, vModel, input), false) // 为每个 input 注册 keyup 响应时间,具体在 this.handleInput 函数中进行处理,改变该函数的 this 指向,并把 vModel 和 input 作为参数传递过去
}
})
}
handleInput(property, input) {
const value = input.value
this._data[property] = value // 把 input 的值赋值给代理对象对应属性
}
}
const app = new MVVM('#app', {
name: 'user1',
age: '11',
email: '11@qq.com',
tel: '124'
})