前言
最近学到了proxy和reflet就想着做一个小案例,这就想到了可以用这个实现vue框架里面的v-model双向数据绑定
实现思路:
-
创建一个 Proxy 对象,用于监听数据对象属性的读取和赋值操作。
-
在数据对象对应的属性被访问时,会触发 Proxy 对象的 get() 方法,我们可以在这个方法中添加必要的逻辑,如:如果是获取属性值,则将该属性值返回,如果是设置属性值,则需要将新值同步到输入框中。
-
在输入框的输入事件中,将输入框的值同步到数据对象中,这里需要注意的是,在数据对象的属性被赋值时,会触发 Proxy 对象的 set() 方法,我们可以在这个方法中添加必要的逻辑,如:更新属性值,并将新值同步到输入框中。
-
还需要注意的是,在使用 Proxy 监听对象的读取和赋值操作时,需要注意对象的嵌套层数,如果是多层嵌套,则需要递归创建 Proxy 对象,以达到监听所有属性和属性值的目的。
<body>
<div>
<label>姓名:</label>
<input type="text" id="inputName" cameel-bind="name">
<p cameel-bind="name">姓名:{{}}</p>
</div>
<div>
<label>年龄:</label>
<input type="text" id="inputAge" cameel-bind="age">
<p cameel-bind="age">年龄:{{data.age}}</p>
</div>
<script>
const data = {
name: "小明",
age: 18
};
const handler = {
get(target, key, receiver) {
return Reflect.get(target, key, receiver)
},
set(target, key, value, receiver) {
console.log(`set ${key} = ${value}`)
Reflect.set(target, key, value, receiver);
render();
return true;
}
};
const proxy = new Proxy(data, handler);
const inputName = document.getElementById("inputName")
const inputAge = document.getElementById("inputAge")
inputName.addEventListener('input', function (e) {
proxy.name = e.target.value;
})
inputAge.addEventListener('input', function (e) {
proxy.age = e.target.value;
})
function render() {
const elList = document.querySelectorAll('[cameel-bind]')
elList.forEach(el => {
const key = el.getAttribute('cameel-bind')
console.log(el.getAttribute('cameel-bind'))
const value = proxy[key]
el.innerText = value
el.value = value;
});
}
render();
</script>
</body>
render函数:
function render() {
const elList = document.querySelectorAll('[cameel-bind]')
elList.forEach(el => {
const key = el.getAttribute('cameel-bind')
console.log(el.getAttribute('cameel-bind'))
const value = proxy[key]
el.innerText = value
el.value = value;
});
}
这里详细解析一下上述函数:
首先我们看到的这个函数名字叫render,显然它是用来渲染页面的,所以它的作用就是用来更新页面上那些被绑定了某个key值的元素的内容。
接下来我们看里面的代码,首先获取了所有带有cameel-bind属性的元素,这里用到了querySelectorAll方法,它是用来获取页面上所有符合指定选择器的元素的列表,这个方法是基于CSS选择器实现的,相信你已经非常熟悉它了,因为这是前端开发必备的知识点啊。而cameel-bind是这里自定义的一个属性,用来绑定元素的内容和数据源。
接着我们来看forEach方法,这是一个用来遍历数组的方法,对于每一个元素执行指定的回调函数。里面的回调函数包含两部分,第一部分是获取这个元素的cameel-bind的属性值,即key。第二部分是用获取到的key去代理对象(proxy)中获取对应的值(value),然后将这个值赋给元素的innerText属性,也就是元素的文本内容。这里用到了innerText属性,它是用来获取或设置元素的文本内容的。
接下来我们看render()函数的执行,它没有任何的参数,这表示它靠的是闭包的机制来获取所有需要渲染的元素。当闭包中的数据发生变化时,我们就需要再次调用这个函数来实现渲染。
render 函数实现解析总结:
- 获取所有带有cameel-bind属性的元素
- 对于每个元素,获取它绑定的key值
- 到代理对象(proxy)中查找这个key对应的value值
- 将value赋给这个元素的innerText属性,更新它的内容
- 执行render()函数,渲染页面,并且可以反复调用它来实现数据的动态更新。
注意事项:
-
修改代理对象的属性时,需要使用Reflect.set()方法,否则代理对象的属性不会被更新。
-
双向绑定的数据必须在data对象中定义。
-
使用Proxy需要判断浏览器是否支持该API。
-
给页面元素绑定cameel-bind属性时,属性值必须是data对象中已定义的属性名,否则页面不会渲染该属性。