欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Vue 中 Object.defineProperty 与 Proxy 实现双向绑定的原理

程序员文章站 2022-07-12 21:54:36
...

实现双向绑定的三要素

  1. 响应式: 如何监听数据变化
  2. 模板引擎: 如何解析HTML代码
  3. 渲染: 如何将监听到的数据变化和解析后的HTML进行渲染

一、Object.defineProperty

通过 Object.defineProperty监听对象属性的改变

实例定义的属性,vue 内部通过 Object.defineProperty重新定义,生成 Observer

// 实例定义的属性
const obj = {
  message: '哈哈哈',
  name: 'jack'
}

// vue 内部流程
Object.keys(obj).forEach(key => {
  let value = obj[key]
  
  Object.defineProperty(obj, key, {
    set(newValue) {
      // 监听 key 的改变
      value = newValue
    },
    get() {
      // 获取 key 对应的值
      return value
    }
  })
})

当数据发生改变时,vue是如何知道?

  1. 利用Object.defineProperty生成的Observer针对对象的属性进行"劫持",一个属性创建一个 Dep 对象,在属性发生变化后通知订阅者

  2. 解析器(Compile)解析模板中的 Directive(指令),获取到哪里用到了属性(订阅者),比如{{name}} {{message}},创建一个观察者watcher添加到对应的Dep 对象中,同时初始化view,在界面上显示

  3. Watcher属于ObserverCompile桥梁,将接收到的Observer产生的数据变化,并根据Compile提供的指令进行视图渲染,使得数据变化促使视图变化

Object.defineProperty的缺陷

  1. 无法监听数组变化
    只有以下七种方法可以检测到数组变化

    push()
    pop()
    shift()
    unshift()
    splice()
    sort()
    reverse()
    
  2. 只能劫持对象的属性,因此需要对对象的每个属性进行遍历,如果属性值也是对象那么需要深度遍历,显然能劫持一个完整的对象是更好的选择

二、Proxy

    var model = {}
    var p = new Proxy(model, {
        get(model, prop) {
            return model[prop]
        },
        set(model, prop, value) {
            model[prop] = value
            //    当 model 中的数据发生改变时, view 中的数据也自动改变
            document.querySelector('[v-bind]').innerHTML = value
        }
    })
    //    当 view 中的数据发生改变时, model 中的数据自动改变
    document.querySelector('[v-model]').oninput = function (e) {
        p.msg = e.target.value
    }    

Proxy 的优点

  1. 可以直接监听对象而非属性
  2. 可以直接监听数组的变化
  3. 返回的是一个新对象,可以只操作新的对象达到目的,而Object.defineProperty只能遍历对象属性直接修改