Vue.js的双向数据绑定是如何实现的?

Vue.js 的双向数据绑定是通过数据劫持结合发布者-订阅者模式实现的。

具体思路是:

  1. 通过Object.defineProperty()来劫持各个属性的setter,getter。
  2. 实现一个Dep类来存放订阅者,并在getter中收集订阅者,在setter中通知订阅者更新视图。
  3. 视图渲染时在对应的指令中添加订阅者,即Watcher对象。
  4. 数据变动时通过发布者-订阅者模式通知各个订阅者,订阅者再响应更新视图。

整个流程可以概括为:数据改变 -> 发布者通知 -> 订阅者响应 -> 视图更新。

例子:

<div id="app">
  <p>{{ message }}</p>
  <input v-model="message">
</div>
var data = {
  message: 'Hello'
}
var vm = new Vue({
  el: '#app',
  data: data
})

当用户输入值更新 message 时,视图会自动渲染。实现原理如下:

  1. 初始化时,将message属性用Object.defineProperty()劫持:
var data = {
  message: 'Hello'
}
var vm = new Vue({
  el: '#app',
  data: data
})

Object.defineProperty(data, 'message', {
  get: function() {
    console.log('get')
    Dep.target && dep.addSub(Dep.target)
    return message
  },
  set: function(newValue) {
    console.log('set')
    message = newValue
    dep.notify()
  }
})
  1. 订阅者Watcher被添加到订阅器dep中:
var watcher = new Watcher(vm, 'message', function(val) {
  element.value = val
})
  1. 当message发生变化时,通知订阅器dep,然后订阅器dep调用订阅者的update()方法进行视图渲染:
set: function(newValue) {
  console.log('set')
  message = newValue
  dep.notify()  // 通知订阅器
} 

// 订阅器调用更新方法
dep.notify() {
  subs.forEach(sub => {
    sub.update()  // 更新视图
  })
}

Vue 的双向数据绑定的原理在于通过数据劫持和发布-订阅模式实现数据和视图的同步更新。