vue需要掌握的原理--响应式原理一

74 阅读2分钟

大家好,我是鼠目。「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战

数据响应式,是MVVM框架的一大特点,也就是通过某种策略感知数据的变化。大家都知道Vue是利用了Js的Object.defineProperty(),通过定义对象的属性getter/setter拦截了对属性的访问。

它是在那一步做了属性的拦截呢?

记不记得在src/core/instance/init.js下,Vue.prototype._init方法中有个initState方法进行组件数据的初始化,让我们继续深入initState函数,src/core/instance/state.js

   export function initState (vm: Component) {
      //在vue的实例下,创建了_watcher
      vm._watchers = []
      const opts = vm.$options
      if (opts.props) initProps(vm, opts.props)
      if (opts.methods) initMethods(vm, opts.methods)
      // 如果,存在options中存在data,对data进行初始化。
      if (opts.data) {
        initData(vm)
      } else {
        //否则直接走observe,observe是干什么的呢?后续再进行介绍。
        observe(vm._data = {}, true /* asRootData */)
      }
      if (opts.computed) initComputed(vm, opts.computed)
      if (opts.watch && opts.watch !== nativeWatch) {
        initWatch(vm, opts.watch)
      }
    }

可以看到,initState函数中,创建了一个watcher数组,并进行了数据初始化,顺序是props->methods->data->computed->watch,所以我们可以在data中直接赋值props传入的数据。现在,让我们直接去看其中最关键的initData。

function initData (vm: Component) {
  let data = vm.$options.data
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {}
  // 判断是否有重名信息 props and methods, 代码省略  
  // proxy 是一层代理, 可以直接通过this.a 访问到this._data.a的数据
  proxy(vm, `_data`, key)

  // observe data
  // 遍历响应式处理
  observe(data, true /* asRootData */)
}

所以看出来了,其实initData中最关键的就是这个observe函数,继续进入

export function observe (value: any, asRootData: ?boolean): Observer | void {
  // 判断是不是对象,或者是不是虚拟dom,不是对象,或者是虚拟dom的情况,直接返回,不做响应式处理
  if (!isObject(value) || value instanceof VNode) {
    return
  }
  // Observer作用?
  // 1.将传入value做响应式处理
  let ob: Observer | void
  // 如果一个对象有__ob__(也就是observer的实例),就说明已经做过响应式处理,则直接返回ob
  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
    ob = value.__ob__
  } else if (
    //这一段干啥的,还不明白
    shouldObserve &&
    !isServerRendering() &&
    (Array.isArray(value) || isPlainObject(value)) &&
    Object.isExtensible(value) &&
    !value._isVue
  ) {
    // 初始化传入需要响应式的对象
    ob = new Observer(value)
  }
  // 这个干啥的,也暂时不明白
  if (asRootData && ob) {
    ob.vmCount++
  }
  return ob
}

observe函数中,它其实也主要只做了一件事情,就是new Observer(value) 那么这个Observer又是何方神圣呢?

    export class Observer {
      value: any;
      //dep
      dep: Dep;
      vmCount: number; // number of vms that have this object as root $data

      constructor (value: any) {
        // 2.此处dep目的?
        // 如果使用Vue.set/delete添加或删除属性,负责通知更新
        this.value = value
        // 
        this.dep = new Dep()
        this.vmCount = 0
        def(value, '__ob__', this)

        // 1.分辨传入对象类型
        if (Array.isArray(value)) {
          // 现代浏览器,覆盖原型,数组执行的方法
          if (hasProto) {
            protoAugment(value, arrayMethods)
          } else {
            copyAugment(value, arrayMethods, arrayKeys)
          }
          this.observeArray(value)
        } else {
          对象执行的方法
          this.walk(value)
        }
      }

      /**
       * Walk through all properties and convert them into
       * getter/setters. This method should only be called when
       * value type is Object.
       */
      walk (obj: Object) {
        const keys = Object.keys(obj)
        for (let i = 0; i < keys.length; i++) {
          defineReactive(obj, keys[i])
        }
      }

      /**
       * Observe a list of Array items.
       */
      observeArray (items: Array<any>) {
        for (let i = 0, l = items.length; i < l; i++) {
          observe(items[i])
        }
      }
    }