实现思路
- 数据 -> 响应式的数据 Object.defineProperty Proxy
- input -> input/keyup -> 事件处理函数的绑定 -> 改变数据
- 相关的DOM -> 数据 => 绑定在一起
操作数据的某个属性 -> 对应DOM就改变
首先建一个html页面
<!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">
<!-- 逻辑改变了数据、数据和视图是相互改变,相互影响的过程 v-model -->
<div>
<input type="text" v-model="name" placeholder="姓名" />
<input type="text" v-model="age" placeholder="年龄" />
</div>
<div>
<p>姓名: <span>{{ name }}</span></p>
<p>年龄: <span>{{ age }}</span></p>
</div>
<button id="btn">改变姓名</button>
</div>
<script src="mvvm.js"></script>
<script>
const app = new MVVM('#app', {
name: '',
age: '',
});
document
.querySelector('#btn')
.addEventListener('click', function () {
app.setData('name', '久奈西野');
});
</script>
</body>
</html>
MVVM.js
class MVVM {
constructor(el, data) {
this.el = document.querySelector(el);
this._data = data;
this.domPool = {}; //作用于dom和数据的绑定
this.init();
}
init() {
this.initData();
this.initDom();
}
initDom() {
this.bindDom(this.el);
this.bindInput(this.el);
}
initData() {
const that = this;
this.directive = {}; //在实例上新增一个data空对象
// 方法1
// for (const key in this._data) {
// Object.defineProperty(this.directive, key, {
// get() {
// // console.log('获取数据:', key, that._data[key]);
// return that._data[key];
// },
// set(newValue) {
// // console.log('设置数据:', key, newValue);
// that.domPool[key].innerHTML = newValue;
// that._data[key] = newValue;
// },
// });
// }
//Proxy方法 方法2
this.directive = new Proxy(this._data, {
get(target, key) {
return Reflect.get(target, key);
},
set(target, key, value) {
that.domPool[key].innerHTML = value;
return Reflect.set(target, key, value);
},
});
}
bindDom(el) {
const childNodes = el.childNodes;
childNodes.forEach((item) => {
// 判断是否为文本节点
if (item.nodeType === 3) {
const _value = item.nodeValue;
if (_value.trim().length) {
let _isValid = /\{\{(.+?)\}\}/.test(_value);
if (_isValid) {
const _key = _value.match(/\{\{(.+?)\}\}/)[1].trim();
this.domPool[_key] = item.parentNode;
item.parentNode.innerText = this.directive[_key] || '';
}
}
}
// 递归
item.childNodes && this.bindDom(item);
});
}
// 为DOM节点添加指令事件
bindInput(el) {
const _allInputs = el.querySelectorAll('input');
_allInputs.forEach((input) => {
const _vModel = input.getAttribute('v-model');
if (_vModel) {
input.addEventListener(
'keyup',
this.handleInput.bind(this, _vModel, input),
false
);
}
});
}
// 更新数据
handleInput(key, input) {
const _value = input.value;
this.directive[key] = _value;
// console.log(this.directive);
}
setData(key, value) {
this.directive[key] = value;
}
}