原生 JavaScript 实现 Vue 的 v-model 双向绑定
在前端开发中,Vue.js 的 v-model 是一个非常强大的功能,在表单中很常见,通过v-model能够轻松实现数据与视图的双向绑定。但你有没有想过,如果不用 Vue,只用原生 JavaScript,怎么来实现类似的功能?今天我们就来尝试如何通过原生 JavaScript 实现一个简单的 v-model 双向绑定。
背景:什么是双向绑定?
双向绑定是指数据模型(Model)和视图(View)之间的同步关系:
- 当用户在输入框中修改内容时,数据模型会自动更新。
- 当数据模型发生变化时,视图也会自动更新。
Vue 的 v-model 就是这种机制的一个典型实现。
代码
HTML 结构
<input type="text" data-bind="message" />
<p data-bind="message"></p>
<input>元素用于接收用户输入。<p>元素用于展示数据模型的内容。- 使用自定义属性
data-bind来标记哪些元素需要绑定到数据模型。data-bind="message"表示这些元素绑定到了message这个数据字段。
核心逻辑
1. 使用 Proxy 实现响应式数据
const data = new Proxy(
{ message: "msg" },
{
set(target, key, val) {
target[key] = val;
updateDom(key, val); // 数据变化后更新 DOM
return true;
},
}
);
- 使用了 ES6 的
Proxy对象来劫持数据的变化,Proxy是vue3的数据劫持方式,vue2用的是Object.defineProperty,他们的区别这里就不多赘述了。当数据模型中的某个字段被修改时,set方法会被触发。 - 在
set方法中,调用updateDom函数,将新的值同步到所有绑定了该字段的 DOM 元素。
2. 更新 DOM 的函数
function updateDom(key, val) {
document.querySelectorAll(`[data-bind=${key}]`).forEach((element) => {
if (element.tagName === "INPUT") {
element.value = val; // 更新输入框的值
} else {
element.textContent = val; // 更新其他元素的内容
}
});
}
updateDom函数的作用是遍历所有绑定了指定字段的 DOM 元素,并根据元素类型(<input>或其他标签)更新其值或内容。- 例如,当
message字段发生变化时,所有data-bind="message"的元素都会被更新。
3. 绑定事件监听器
document.querySelectorAll("[data-bind]").forEach((element) => {
const key = element.dataset.bind;
if (element.tagName === "INPUT") {
element.value = data[key]; // 初始化输入框的值
} else {
element.textContent = data[key]; // 初始化其他元素的内容
}
element.addEventListener("input", (e) => {
data[key] = e.target.value; // 修改数据模型,触发 Proxy
});
});
- 我们通过
querySelectorAll获取所有带有data-bind属性的元素。 - 根据元素类型初始化其值或内容。
- 对于
<input>元素,我们添加了一个input事件监听器。当用户输入内容时,事件回调会更新数据模型,从而触发Proxy的set方法。
4. 模拟外部修改数据
setTimeout(() => {
data.message = "程序修改的值";
}, 2000);
- 为了验证数据模型的变化是否能同步到视图,在 2 秒后通过代码修改了
data.message的值。 - 此时,
Proxy的set方法会被触发,调用updateDom函数更新所有绑定的 DOM 元素。
运行效果
- 页面加载时,输入框显示初始值
"msg",下方的<p>标签也显示"msg"。 - 当用户在输入框中输入内容时,
<p>标签的内容会实时更新。 - 2 秒后,程序将
message的值修改为"程序修改的值",此时输入框和<p>标签的内容都会同步更新。