美文网首页
数据绑定

数据绑定

作者: Yang152412 | 来源:发表于2017-08-07 15:43 被阅读234次

    数据绑定模块 最核心的三个类: Observer,Watcher,Dep

    observer是Vue核心中最重要的一个模块(个人认为),能够实现视图与数据的响应式更新,底层全凭observer的支持。

    observer模块在Vue项目中的代码位置是src/core/observer,模块共分为这几个部分:

    • Observer: 数据的观察者,让数据对象的读写操作都处于自己的监管之下
    • Watcher: 数据的订阅者,数据的变化会通知到Watcher,然后由Watcher进行相应的操作,例如更新视图
    • Dep: Observer与Watcher的纽带,当数据变化时,会被Observer观察到,然后由Dep通知到Watcher

    示意图如下:

    Observer

    Observer类定义在src/core/observer/index.js中,先来看一下Observer的构造函数

    constructor (value: any) {
      this.value = value
      this.dep = new Dep()
      this.vmCount = 0
      def(value, '__ob__', this)
      if (Array.isArray(value)) {
          const augment = hasProto
          ? protoAugment
          : copyAugment
        augment(value, arrayMethods, arrayKeys)
        this.observeArray(value)
      } else {
        this.walk(value)
      }
    }
    

    value是需要被观察的数据对象,在构造函数中,会给value增加ob属性,作为数据已经被Observer观察的标志。如果value是数组,就使用observeArray遍历value,对value中每一个元素调用observe分别进行观察。如果value是对象,则使用walk遍历value上每个key,对每个key调用defineReactive来获得该key的set/get控制权。

    解释下上面用到的几个函数的功能:

    • observeArray: 遍历数组,对数组的每个元素调用observe
    • observe: 检查对象上是否有ob属性,如果存在,则表明该对象已经处于Observer的观察中,如果不存在,则new Observer来观察对象(其实还有一些判断逻辑,为了便于理解就不赘述了)
    • walk: 遍历对象的每个key,对对象上每个key的数据调用defineReactive
    • defineReactive: 通过Object.defineProperty设置对象的key属性,使得能够捕获到该属性值的set/get动作。一般是由Watcher的实例对象进行get操作,此时Watcher的实例对象将被自动添加到Dep实例的依赖数组中,在外部操作触发了set时,将通过Dep实例的notify来通知所有依赖的watcher进行更新。
      如果不太理解上面的文字描述可以看一下图:

    Dep

    Dep是Observer与Watcher之间的纽带,也可以认为Dep是服务于Observer的订阅系统。Watcher订阅某个Observer的Dep,当Observer观察的数据发生变化时,通过Dep通知各个已经订阅的Watcher。

    Dep提供了几个接口:

    • addSub: 接收的参数为Watcher实例,并把Watcher实例存入记录依赖的数组中
    • removeSub: 与addSub对应,作用是将Watcher实例从记录依赖的数组中移除
    • depend: Dep.target上存放这当前需要操作的Watcher实例,调用depend会调用该Watcher实例的addDep方法,addDep的功能可以看下面对Watcher的介绍
    • notify: 通知依赖数组中所有的watcher进行更新操作

    Watcher

    Watcher是用来订阅数据的变化的并执行相应操作(例如更新视图)的。Watcher的构造器函数定义如下:

    constructor (vm, expOrFn, cb, options) {
      this.vm = vm
      vm._watchers.push(this)
      // options
      if (options) {
        this.deep = !!options.deep
        this.user = !!options.user
        this.lazy = !!options.lazy
        this.sync = !!options.sync
      } else {
        this.deep = this.user = this.lazy = this.sync = false
      }
      this.cb = cb
      this.id = ++uid // uid for batching
      this.active = true
      this.dirty = this.lazy // for lazy watchers
      this.deps = []
      this.newDeps = []
      this.depIds = new Set()
      this.newDepIds = new Set()
      this.expression = process.env.NODE_ENV !== 'production'
        ? expOrFn.toString()
        : ''
      if (typeof expOrFn === 'function') {
        this.getter = expOrFn
      } else {
        this.getter = parsePath(expOrFn)
        if (!this.getter) {
          this.getter = function () {}
          process.env.NODE_ENV !== 'production' && warn(
            `Failed watching path: "${expOrFn}" ` +
            'Watcher only accepts simple dot-delimited paths. ' +
            'For full control, use a function instead.',
            vm
          )
        }
      }
      this.value = this.lazy
        ? undefined
        : this.get()
    }
    

    参数中,vm表示组件实例,expOrFn表示要订阅的数据字段(字符串表示,例如a.b.c)或是一个要执行的函数,cb表示watcher运行后的回调函数,options是选项对象,包含deep、user、lazy等配置。

    watcher实例上有这些方法:

    • get: 将Dep.target设置为当前watcher实例,在内部调用this.getter,如果此时某个被Observer观察的数据对象被取值了,那么当前watcher实例将会自动订阅数据对象的Dep实例
    • addDep: 接收参数dep(Dep实例),让当前watcher订阅dep
    • cleanupDeps: 清除newDepIds和newDep上记录的对dep的订阅信息
    • update: 立刻运行watcher或者将watcher加入队列中等待统一flush
    • run: 运行watcher,调用this.get()求值,然后触发回调
    • evaluate: 调用this.get()求值
    • depend: 遍历this.deps,让当前watcher实例订阅所有dep
    • teardown: 去除当前watcher实例所有的订阅

    observe(value):

    function observe (value, asRootData) {
      if (!isObject(value)) {
        return
      }
      var ob;
      if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
        ob = value.__ob__;
      } else if (
        observerState.shouldConvert &&
        !isServerRendering() &&
        (Array.isArray(value) || isPlainObject(value)) &&
        Object.isExtensible(value) &&
        !value._isVue
      ) {
        ob = new Observer(value);
      }
      if (asRootData && ob) {
        ob.vmCount++;
      }
      return ob
    }
    

    给 value 定义一个 __ob__ 作为 Observer 的 key

    value:
        __ob__:Observer
        key1:
            get: function reactiveGetter () {
                  var value = getter ? getter.call(obj) : val;
                  if (Dep.target) {
                    dep.depend();
                    if (childOb) {
                      childOb.dep.depend();
                    }
                    if (Array.isArray(value)) {
                      dependArray(value);
                    }
                  }
                  return value
                },
            set: function reactiveSetter (newVal) {
              var value = getter ? getter.call(obj) : val;
              /* eslint-disable no-self-compare */
              if (newVal === value || (newVal !== newVal && value !== value)) {
                return
              }
              /* eslint-enable no-self-compare */
              if (process.env.NODE_ENV !== 'production' && customSetter) {
                customSetter();
              }
              if (setter) {
                setter.call(obj, newVal);
              } else {
                val = newVal;
              }
              childOb = observe(newVal);
              dep.notify();
            }
    
    
    __ob__:
        value: value
        dep: dep
    
    

    转载自:https://segmentfault.com/a/1190000008377887

    相关文章

      网友评论

          本文标题:数据绑定

          本文链接:https://www.haomeiwen.com/subject/vdlalxtx.html