乐趣区

关于vue.js:Vue2响应式原理解析完结篇侦听属性和总结

Hi 大家好,假期高兴鸭~ 咳咳,在后面两篇咱们从设计登程讲了一下 Vue2 的响应式原理和实现,还有计算属性的具体解析等等。这一篇呢就是这个系列的最初一篇了,咱们来聊一下侦听属性和 vm.$watch,再回到设计来总结一下 Vue2 的响应式。如果没有看过后面两篇的敌人先看了后面的再来哈,传送门:Vue2 响应式原理解析(一):从设计登程,Vue2 响应式原理解析(二):计算属性揭秘。

侦听属性 watch

对于侦听属性的应用我就不多了,置信大家都很纯熟,无非就是定义个函数,当要监听的值发生变化时会回调这个函数。咱们间接来康康要害代码是怎么实现的。关上 src/core/instance/state.js 文件,找到 initState

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) // 侦听属性的初始化是在最初。}
}

这个函数外面能够看到相熟的 initDatainitComputed,后面曾经讲过了。initWatch 就是侦听属性初始化的函数了,这里留神一下 initWatch 是在最初调用的并传入了 vm 对象(其实就是要监听 vm 上的属性), 这意味着侦听属性也是能够侦听到计算属性的变动哟 initWatch 的内容很简略,咱们只看要害的代码:

function initWatch (vm: Component, watch: Object) {for (const key in watch) {const handler = watch[key]
    if (Array.isArray(handler)) {for (let i = 0; i < handler.length; i++) {createWatcher(vm, key, handler[i])
      }
    } else {
      // 看这里。createWatcher(vm, key, handler)
    }
  }
}

initWatch 遍历咱们定义的 watch 对象属性,拿到每个属性的侦听函数 handler,并对其调用 createWatcher

function createWatcher (
  vm: Component,
  expOrFn: string | Function,
  handler: any,
  options?: Object
) {if (isPlainObject(handler)) {
    options = handler
    handler = handler.handler
  }
  if (typeof handler === 'string') {handler = vm[handler]
  }

  // 其实侦听属性最初就是用 $watch api 实现的。return vm.$watch(expOrFn, handler, options)
}

到这里咱们发现原来侦听属性就是利用 vm.$watch 来实现的啦。

vm.$watch

&dollar;watch 函数是能够在 Vue 对象上调用的,所以定义在了 Vue 对象的原型上,具体在 stateMixin 函数中:

export function stateMixin (Vue: Class<Component>) {
  // ...

  Vue.prototype.$watch = function (
    expOrFn: string | Function,
    cb: any,
    options?: Object
  ): Function {
    const vm: Component = this
    // ...
    
    options = options || {}
    options.user = true
    const watcher = new Watcher(vm, expOrFn, cb, options)

    // 立刻求值
    if (options.immediate) {
      try {cb.call(vm, watcher.value)
      } catch (error) {// ...}
    }
    return function unwatchFn () {watcher.teardown()
    }
  }
}

这里咱们看到 vm.$watch 其实是生成了 Watcher 对象,有这几个中央要康康:

  • expOrFn,这里是须要监听的属性值。
  • options.user,表明是用户定义的,watcher 更新时会调用用户定义的回调函数 cb。
  • options.immediate,立刻求值,一开始就会传递以后值到定义的侦听函数。
  • unwatchFn,能够手动敞开监听(当然定义的侦听属性不须要这个了)。

expOrFn

这里我重点说一下 expOrFn。expOrFn 是能够反对属性表达式的,依照 Vue 文档的说法:

察看 Vue 实例上的一个表达式或者一个函数计算结果的变动。

也就是说能够像这样去设置:

vm.$watch('a.b', function (newVal, oldVal) {// 属性 a 扭转或者 a.b 扭转都会触发})

vm.$watch(function () {return this.c + this.c},
  function (newVal, oldVal) {// 属性 c 扭转或者 d 扭转都会触发}
)

第一种状况下 expOrFn 是表达式。在 src/core/observer/watcher.js 中找到 Watcher 的构造函数:

// ...
if (typeof expOrFn === 'function') {this.getter = expOrFn} else {this.getter = parsePath(expOrFn)
  // ...
}
// ...

咱们晓得 watcher.getter 要求是个函数,parsePathsrc/core/util/lang.js 外面:

export function parsePath (path: string): any {
  // ...
  const segments = path.split('.')
  return function (obj) {for (let i = 0; i < segments.length; i++) {if (!obj) return
      obj = obj[segments[i]]
    }
    return obj
  }
}

parsePath 返回的的确是一个函数,内容是依照表达式的门路程序去逐渐取得门路上的属性(对象)以及最初的值。这里咱们回顾下 defineReactive,按程序去拜访表达式门路上的属性会触发属性的 get,这样就建设了依赖关系(门路上的所有属性都会),当波及的属性变动时就会告诉 watcher 了~ 同理第二种状况下是一个函数也是一样的。剩下的怎么去告诉 watcher 更新后面第一篇曾经介绍过了,这里就不多说了。

设计总结

到这里 Vue2 响应式的次要内容解析就完结了,咱们看了很多的代码,是时候来总结一下啦。不晓得大家看源码学习的目标和感触是什么,我呢次要是关注作者的设计用意和衡量侧重点,当然还有一些实现的技巧之类。第一篇咱们从设计登程,最初呢当然也要从设计来总结一下从 Vue2 响应式中学到了什么。

可重用的模块

Vue2 响应式实现的一个很赞的中央是把这套货色独立了进去,形象出了 DepWatcher 等要害定义,基本上如果你是 Web 利用都能够间接拿去用。这通知咱们在实现之前要多思考重用和形象,这样你的实现能力施展更大的价值。

奇妙的双向依赖设计与实现

在观察者模式中,个别咱们只会在被观察者上记录观察者列表,等到须要时去告诉观察者即可。而在 Vue2 中因为应用场景下依赖关系会发生变化(依赖关系收集在求值过程中),所以采纳了双向依赖的设计与实现。这通知咱们设计模式不是死的,依据你想达到的目标去做灵便调整,我感觉这是 Vue2 响应式设计十分精彩的一个中央~

观察者模式的角色权重

从 Vue2 的设计和实现中咱们能够看到,观察者模式中被观察者(属性和 Dep)和观察者(Watcher)的角色权重是不同的,从代码量也能够感触进去。被观察者次要是实现依赖的建设和告诉机制,更形象一些;而观察者则要依据理论场景退出更多的功能设计与实现,比方 Vue 中的计算属性缓存和 expOrFn 等。这也是揭示咱们在实现本人的观察者模式场景中,具体的内容应该放在哪里有个参考。

最初

到这里 Vue2 响应式我想哔哔的货色曾经全副讲完了,心愿能给大家一点参考吧,程度无限,了解和解读难免会有错漏,欢送指出哇。最近 Vue3.0 One Piece 也正式公布了,当前偶也会继续写一些 Vue3 的相干东东,下次再见~

欢送 star 和关注我的 JS 博客:小声比比 Javascript

退出移动版