Skip to content

浅析defineProperty与Proxy实现的双向绑定

文章内容总结自Vue官网 深入响应式原理

🔰 Vue2的响应式原理

image.png

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter

每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

Vue2的响应式原理,利用的是 Object.defineProperty()setter 属性:

defineProperty() 方法用于精确定义一个对象的属性,能够指定属性的各种特征,其中的 set 属性能够为对象指定一个 setter 函数,每次该属性的值发生修改,就会调用此函数。

更多可以配置的属性请参看:什么是对象的数据属性描述符?存储属性描述符?

这也是Vue2实现响应式数据、数据双向绑定的原理。

可以使用此方法实现一个简单的数据双向绑定的Demo:

image.png

  • 输入框内的内容改变,.vBox 展示的文本会随之改变。
  • 点击按钮修改 vm.text,输入框内的值和 .vBox 的文本都会发生改变。
html
  <body>
    <input type="text" id="input" />
    <button onclick="vm.text='Hello, World.'">Modify vm.text</button>
    <div class="vBox"></div>
    <script>
      // 定义响应式数据
      let vm = {}
      Object.defineProperty(vm, "text", {
        set: (value) => {
          // 对象属性值被修改时,setter函数被自动触发
          document.querySelector("#input").value = value
          document.querySelector(".vBox").innerHTML = value
        },
      })
      // 监听输入行为
      document.querySelector("#input").addEventListener("input", (e) => {
        vm.text = e.target.value
      })
    </script>
  </body>

通过 defineProperty 实现的响应式,不能检测数组和对象的变化:

对于对象:

Vue 无法检测 property 的添加或移除。

var vm = new Vue({ data:{ a:1 } }) // vm.a 是响应式的 vm.b = 2 // vm.b 是非响应式的

对于数组:

  1. 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
  2. 当你修改数组的长度时,例如:vm.items.length = newLength

🔰 Vue3的响应式原理

Released under the MIT License.