vue watch val newval_Vue 源码浅析:数据动态响应-观察者对象 Observer

261bd007bc2342f5b9ca98d1d69930ea

继续初始化中未完的内容

初始化流程中,还未提到 initState,这个方法涉及 vue 中有关数据状态的一些操作:

export function initState (vm: Component) {  vm._watchers = []  const opts = vm.$options  if (opts.props) initProps(vm, opts.props)  if (opts.methods) initMethods(vm, opts.methods)  if (opts.data) {    initData(vm)  } else {    observe(vm._data = {}, true /* asRootData */)  }  if (opts.computed) initComputed(vm, opts.computed)  if (opts.watch && opts.watch !== nativeWatch) {    initWatch(vm, opts.watch)  }}

initState 方法内涉及 Vue 主要几个 options 属性的初始化,他们具备数据动态响应特性,我们大致看下他们的方法的内部结构:

initProps

function initProps (vm: Component, propsOptions: Object) {  //...  defineReactive(props, key, value)}

initData

function initData (vm: Component) {  //...  observe(data, true /* asRootData */)}

initComputed

function initComputed (vm: Component, computed: Object) {  //...  watchers[key] = new Watcher(    vm,    getter || noop,    noop,    computedWatcherOptions  )}

initWatch

function initWatch (vm: Component, watch: Object) {  //...  createWatcher(vm, key, handler[i])}

注意,没有贴出 initMethods 方法,因为如果它和 props 重名,会覆盖掉 props 的定义。所以它不具备数据响应功能。只是因为流程关系,放在了 props 之后。

下面开始 Vue 里的数据响应说明。

从 initData 开始,observe 观察对象

因为 data 是我们用的最多的属性,同时所以的数据都在此定义,initData 也是 vue 初始化状态中最代表性的方法,懂了它,其他的初始化方法也会很快理解。

先是做一系列和 props、methods 的校验 warn 判断,最后执行 observe 方法:

function initData (vm: Component) {  //...  observe(data, true /* asRootData */)}

observe 翻译为:观察。此处它的入参是 data,所以会观察 data 对象。也是整个数据响应的入口。

所以抱着这个思路来看后续的代码:

export function observe (value: any, asRootData: ?boolean): Observer | void {  if (!isObject(value) || value instanceof VNode) {    return  }  let ob: Observer | void  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)  }  //...  return ob}

此方法将获得 ob 对象,此对象将从 Observer 观察者中创建获取 or 从当前 value 中的 _ ob_ 属性中获取。

注意,只有数组,对象等复杂类型数据才会被创建观察者对象。要是像普通的 Number、String 将不会被观察。

观察者 Observer

下面是 Observer 类方法的代码:

export class Observer {  value: any;  dep: Dep;  vmCount: number; // number of vms that have this object as root $data  constructor (value: any) {    this.value = value    this.dep = new Dep()    this.vmCount = 0    def(value, '__ob__', this)    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) {    for (let i = 0, l = items.length; i < l; i++) {      observe(items[i])    }  }}

所有的内容将围绕构造器 constructor 开始。


通过 def 方法,定义 _ob_ 属性

构造器中会通过 def 方法来定义 _ob_ 属性:

constructor (value: any) {  def(value, '__ob__', this)}

def 方法中,将见到 vue 所用"活"的 defineProperty 方法:

export function def (obj: Object, key: string, val: any, enumerable?: boolean) {  Object.defineProperty(obj, key, {    value: val,    enumerable: !!enumerable,    writable: true,    configurable: true  })}

通过 defineProperty 原生方法,往当前 value 对象(data)新添加 _ ob_ 属性,并且设置相关的对象属性描述特征。

解释下 def 的传入参数:obj 是我们 vm.$options 中设置的 data 对象,key _ ob_ 属性,val Observer this 引用(属性包括:value,dep,vmCount):

dfde189bbc2448078dc86ef6b5ec1a7c

通过 walk 方法,遍历 data 属性

之后,构造器最后通过 walk 方法,将 vm.$options.data 上所有的对象属性统统执行 defineReactive 方法:

constructor (value: any) {  //...  this.walk(value)}
walk (obj: Object) {  const keys = Object.keys(obj)  for (let i = 0; i < keys.length; i++) {    defineReactive(obj, keys[i])  }}

通过 defineReactive 对 data 属性赋予动态响应能力

因为涉及 Dep 模块,先大概了解此方法的简化结构:

export function defineReactive (  obj: Object,  key: string,  val: any,  customSetter?: ?Function,  shallow?: boolean) {  const dep = new Dep()  //...  Object.defineProperty(obj, key, {    enumerable: true,    configurable: true,    get: function reactiveGetter () {      //...    },    set: function reactiveSetter (newVal) {      //...    }  })}

传入 defineReactive 的 obj 对象会是这个样子(这也是 def 方法执行后的结果):

7ad19a073ec742afaacb5a93589bac28

接下来看 defineReactive 内部的逻辑。

创建 Dep 对象

在这里会创建一个新的 Dep 依赖订阅对象:

const dep = new Dep()

初始化已定义的 getter/setter

获取 data 对象上属性的具体描述说明,并且取得 getter/setter 访问器属性:

const property = Object.getOwnPropertyDescriptor(obj, key)const getter = property && property.getconst setter = property && property.set

注意,会根据 arguments 参数预先执行 setter 方法:

if ((!getter || setter) && arguments.length === 2) {  val = obj[key]}

然后会重新对 data 对象上的属性来定义 getter/setter 访问器属性:

let childOb = !shallow && observe(val)  Object.defineProperty(obj, key, {    enumerable: true,    configurable: true,    get: function reactiveGetter () {      //...    },    set: function reactiveSetter (newVal) {      //...    }  })

reactiveGetter

先看 getter 的定义:

Object.defineProperty(obj, key, {    enumerable: true,    configurable: true,    get: function reactiveGetter () {      const value = getter ? getter.call(obj) : val      if (Dep.target) {        dep.depend()        if (childOb) {          childOb.dep.depend()          if (Array.isArray(value)) {            dependArray(value)          }        }      }      return value    }  //...}

当模板解析后,就会调用其中设置的变量(如下,message),从而触发 get 方法:

{{ message }}

这里会判断 Dep.target ,是否有监听对象 Watcher。

if (Dep.target) {  //...}

具体哪里对 Dep.target 进行了赋值,见下篇中的:依赖订阅器 Dep > 哪里设置 Dep target

我们先假设 Dep.target 存在,那么将执行 dep.depend 方法;如果子属性可以被观察,也将进行依赖解析:

if (Dep.target) {  dep.depend()  if (childOb) {    childOb.dep.depend()    //..  }}

Dep 模块具体再看 depend 的作用。

reactiveSetter

setter 相对简单,主要流程如下:

  • 当对 data 中的属性更新时,先触发上面的 getter 方法。
  • 然后判断设置的 newVal 和之前的 value 有无不同,如果未发生改变则直接 return。
  • 如果有定义的 setter 方法,则会执行直接更新 data 对象;反之,对 val 赋予新值。
  • 最后对 newVal 进行 observe 观察,并通知 dep.notify 。
set: function reactiveSetter (newVal) {  const value = getter ? getter.call(obj) : val  /* eslint-disable no-self-compare */  if (newVal === value || (newVal !== newVal && value !== value)) {    return  }  // #7981: for accessor properties without setter  if (getter && !setter) return  if (setter) {    setter.call(obj, newVal)  } else {    val = newVal  }  childOb = !shallow && observe(newVal)  dep.notify()}

总结

上面是执行 observe 观察方法后,执行的相关过程,通过 Observer 方法类创建观察者对象,每个观察者都具备 __ob__属性。

接着通过 defineReactive 对 options.data 上的属性进行数据动态响应的设置,其核心就是通过 getter/setter 建立起观察机制。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值