实现如下图所示的双向数据绑定的案例
代码如下:
<!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">
<input type="text" v-model="username" placeholder="请输入用户名">
<input type="text" v-model="password" placeholder="请输入密码">
<input type="text" v-model="age" placeholder="年龄">
<input type="text" v-model="sex" placeholder="性别">
<div class="content">
<p>姓名:<span>{{ username }}</span></p>
<p>密码:<span>{{ password }}</span></p>
<p>年龄:<span>{{ age }}</span></p>
<p>性别:<span>{{ sex }}</span></p>
</div>
</div>
<script src="./index2.js"></script>
<script>
const vm = new BindData('#app', {
username: '123',
password: '',
age: '',
sex: '',
})
vm.init(); // 执行初始化函数
console.log(vm);
</script>
</body>
</html>
// 1. 绑定响应式数据
// 初始化数据
// 2. 数据变化 -> 视图跟新
class BindData {
constructor (el, data) {
if (!el) throw new Error('error!');
this.$el = document.querySelector(el)
this._data = data;
// 存放响应式数据
this.data = {};
// 保存初始化时 {{key}} 对应的节点
this.domPool = {};
}
init () {
// 1. 初始化响应式数据
this.initState();
this.initDom();
}
// 掌管dom方法
initDom () {
this.initInput();
this.initContent(this.$el);
}
initInput () {
const el = this.$el;
const allChilds = el.getElementsByTagName('input');
for (let i = 0; i < allChilds.length; i++) {
const item = allChilds[i];
const vModel = item.getAttribute('v-model');
item.value = this.data[vModel] || '';
// 绑定事件
this.bindInputEvent(item, vModel);
}
}
initContent (el) {
const allChilds = el.childNodes,
data = this.data;
[...allChilds].forEach((item) => {
// 文本节点
if (item.nodeType === 3) {
const value = item.nodeValue;
if (value && value.length > 0) {
let key = value.match(/\{\{(.*?)\}\}/);
if (key) {
key = key[1].trim();
// 有{{key}} 的节点
item._initTemplate = value;
this.domPool[key] = item;
item.nodeValue = item.nodeValue.replace(/\{\{(.*?)\}\}/, data[key] || undefined);
}
}
} else {
this.initContent(item);
}
})
}
// 绑定事件
bindInputEvent (input, key) {
const data = this.data;
input.addEventListener ('input', function () {
const value = this.value.trim();
// 修改响应式数据
data[key] = value;
}, false)
}
initState () {
const vm = this,
_data = this._data;
// 存放响应式数据
this.data = new Proxy(_data, this.handlerProxy())
}
// 代理配置
handlerProxy () {
const _self = this;
const get = createGetter(),
set = createSetter();
function createGetter () {
return function (target, key, reciver) {
console.log('响应式数据读取', key);
return Reflect.get(target, key);
}
}
function createSetter () {
return function (target, key, value, reciver) {
console.log(`数据设置${key}`);
const res = Reflect.set(target, key, value);
// 数据变化
_self.renderContent(key, value);
return res;
}
}
return {
get,
set,
}
}
renderContent (key, value) {
const domPool = this.domPool;
console.log(key, value);
domPool[key].nodeValue = value;
}
}