原生 JavaScript 实现 Vue 的 v-model 双向绑定

180 阅读3分钟

原生 JavaScript 实现 Vue 的 v-model 双向绑定

在前端开发中,Vue.js 的 v-model 是一个非常强大的功能,在表单中很常见,通过v-model能够轻松实现数据与视图的双向绑定。但你有没有想过,如果不用 Vue,只用原生 JavaScript,怎么来实现类似的功能?今天我们就来尝试如何通过原生 JavaScript 实现一个简单的 v-model 双向绑定。


背景:什么是双向绑定?

双向绑定是指数据模型(Model)和视图(View)之间的同步关系:

  1. 当用户在输入框中修改内容时,数据模型会自动更新。
  2. 当数据模型发生变化时,视图也会自动更新。

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 事件监听器。当用户输入内容时,事件回调会更新数据模型,从而触发 Proxyset 方法。

4. 模拟外部修改数据
setTimeout(() => {
  data.message = "程序修改的值";
}, 2000);
  • 为了验证数据模型的变化是否能同步到视图,在 2 秒后通过代码修改了 data.message 的值。
  • 此时,Proxyset 方法会被触发,调用 updateDom 函数更新所有绑定的 DOM 元素。

运行效果

  1. 页面加载时,输入框显示初始值 "msg",下方的 <p> 标签也显示 "msg"
  2. 当用户在输入框中输入内容时,<p> 标签的内容会实时更新。
  3. 2 秒后,程序将 message 的值修改为 "程序修改的值",此时输入框和 <p> 标签的内容都会同步更新。

初始内容

image.png

2s后

image.png