深入双向数据绑定原理

来源:互联网 发布:表白网整站html源码 编辑:程序博客网 时间:2024/04/27 00:03

首先附上一篇好文
Vue的双向数据绑定的原理是什么呢,首先讲一下ES5的这个修改属性的方法Object.defineProperty():

这个方法接收三个参数,属性所在的对象obj,属性的名字key,一个描述符对象option;

  1. 当修改的是数据属性时,option包含:configurable(能否修改、删除delete)、enumerable(能否通过for-in遍历属性)、writable(能否修改属性的值)、value(属性的数据值)
  2. 当修改的是访问器属性时,option包含:configurable、enumerable、get(读取属性时调用的函数)、set(写入属性时调用的函数)

这里用的当然是访问器属性啦

当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/setter。用户看不到 getter/setter,但是在内部它们让 Vue 追踪依赖,在属性被访问和修改时通知变化。每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新。

接下来通过代码说说双向数据绑定是如何实现的吧?

import Watcher from '../watcher'import {observe} from "../observer"export default class Vue {  constructor (options={}) {    //这里简化了。。其实要merge    this.$options=options    //这里简化了。。其实要区分的    let data = this._data=this.$options.data    Object.keys(data).forEach(key=>this._proxy(key))    observe(data,this)  }  $watch(expOrFn, cb, options){    new Watcher(this, expOrFn, cb)  }  _proxy(key) {    var self = this    Object.defineProperty(self, key, {      configurable: true,      enumerable: true,      get: function proxyGetter () {        return self._data[key]      },      set: function proxySetter (val) {        self._data[key] = val      }    })  }}
  1. observe的对象:通过递归,将它所有的属性,包括子属性的属性,都给加上set和get,
    这样的话,给这个对象的某个属性赋值,就会触发set。
//observer.jsexport default class  Observer{  constructor(value) {    this.value = value    this.walk(value)  }  //递归。。让每个字属性可以observe  walk(value){    Object.keys(value).forEach(key=>this.convert(key,value[key]))  }  convert(key, val){    defineReactive(this.value, key, val)  }}export function defineReactive (obj, key, val) {  var dep = new Dep()  var childOb = observe(val)  Object.defineProperty(obj, key, {    enumerable: true,    configurable: true,    get: ()=>{      // 说明这是watch 引起的      if(Dep.target){        dep.addSub(Dep.target)      }      return val    },    set:newVal=> {            var value =  val      if (newVal === value) {          return      }      val = newVal      childOb = observe(newVal)//如果新赋值的值是个复杂类型。再递归它,加上set/get。      dep.notify()     }  })}export function observe (value, vm) {  if (!value || typeof value !== 'object') {    return  }  return new Observer(value)}export default class Dep {  constructor() {    this.subs = []  }  addSub(sub){    this.subs.push(sub)  }  notify(){    this.subs.forEach(sub=>sub.update())  }}
//watcher.jsexport default class Watcher {  constructor(vm, expOrFn, cb) {    this.cb = cb    this.vm = vm    //此处简化.要区分fuction还是expression,只考虑最简单的expression    this.expOrFn = expOrFn    this.value = this.get()  }  update(){    this.run()  }  run(){    const  value = this.get()    if(value !==this.value){      this.value = value      this.cb.call(this.vm)    }  }  get(){    //此处简化。。要区分fuction还是expression    const value = this.vm._data[this.expOrFn]    Dep.target = null    return value  }}
原创粉丝点击