从源码看vue响应式原理

前言

众所周知,Vue 是一个 MVVM 框架,它最基本的特征就是数据的双向绑定,在改变数据模型的时候更新视图,视图改变更新数据模型。Vue 上手快速、简单好用,再加上文档丰富全面,Vue 现在已经成为了市面上最流行前端框架之一。但是我们对 Vue 的了解不能仅仅只停留在应用层面上,我们还要了解它的内部原理,为什么这样设计,这样设计的优缺点是什么。我们去了解 Vue 源码,一方面是为了在我们遇到一些比较复杂的问题的时候,我们可以从源码的角度去思考问题;另一方面,了解了很多技术原理之后,或许某一天,你也能创造出一款同样优秀的框架,也说不定呢。

响应式原理的入口

initState

首先我们来看一下 /core/instance/state.js 下面的 initState 函数

export function initState (vm: Component) {
   
  vm._watchers = []
  const opts = vm.$options
  if (opts.props) initProps(vm, opts.props) // 如果有 props ,初始化 props
  if (opts.methods) initMethods(vm, opts.methods) // 如果有 methods ,初始化 methods 里面的方法
  if (opts.data) {
    // 如果有 data 的话,初始化,data;否则响应一个空对象
    initData(vm)
  } else {
   
    observe(vm._data = {
   }, true /* asRootData */)
  }
  if (opts.computed) initComputed(vm, opts.computed) // 如果有 computed ,初始化 computed
  if (opts.watch && opts.watch !== nativeWatch) {
    // 如果有 watch ,初始化 watch
    initWatch(vm, opts.watch)
  }
}

在 initState 函数中,初始化了一些 Vue 的属性,我们现在只需要关注 data 就行,我们一个看到,如果在我们的 Vue 文件中有 data 这个属性的话,那么就会直接初始化 data,否则的话,就会相应一个空的对象。

initData

下面我们再来看看 initData 这个方法

function initData (vm: Component) {
   
  let data = vm.$options.data
  // 初始化 _data,组件中 data 是函数,调用函数返回结果
  // 否则直接返回 data
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {
   }
  if (!isPlainObject(data)) {
   
    data = {
   }
    process.env.NODE_ENV !== 'production' && warn(
      'data functions should return an object:\n' +
      'https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function',
      vm
    )
  }
  // proxy data on instance
  // 获取 data 中的所有属性
  const keys = Object.keys(data)
  // 获取 props / methods
  const props = vm.$options.props
  const methods = vm.$options.methods
  let i = keys.length
  // 判断 data 上的成员是否和  props/methods 重名
  while (i--) {
   
    const key = keys[i]
    if (process.env.NODE_ENV !== 'production') {
   
      if (methods && hasOwn(methods, key)) {
   
        warn(
          `Method "${
     key}" has already been defined as a data property.`,
          vm
        )
      }
    }
    if (props && hasOwn(props, key)) {
   
      process.env.NODE_ENV !== 'production' && warn(
        `The data property "${
     key}" is already declared as a prop. ` +
        `Use prop default value instead.`,
        vm
      )
    } else if (!isReserved(key)) {
   
      proxy(vm, `_data`, key)
    }
  }
  // observe data
  // 响应式处理
  observe(data, true /* asRootData */)
}

从 initData 这个函数中我们能看出,如果 data 是一个函数的话,那么就执行 data 方法返回里面的对象,如果是一个对象的话,就直接返回。然后会遍历 data 里面的 key,看看是否和 props 和 methods 重名,如果重名就报出已经定义过相同数据的错误;如果不重名的话,就调用 observe 函数,observe 就是响应式的入口,它的第一个参数就是需要响应式处理的数据,第二个参数是标识这个数据是否为根数据。

observe

observe 是响应式的入口,下面我们来看看这个函数中都做了什么

export function observe (value: any, asRootData: ?boolean): Observer | void {
   
  // 判断 value 是否是对象
  if (!isObject(value) || value instanceof VNode) {
   
    return
  }
  let ob: Observer | void
  // 如果 value 有 __ob__(observer对象) 属性 结束
  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
  ) {
   
    // 创建一个 Observer 对象
    ob = new Observer(value)
  }
  /*如果是根数据则计数,后面Observer中的observe的asRootData非true*/
  if (asRootData && ob) {
   
    ob.vmCount++
  }
  return ob
}

observe 函数中首先会判断传进来的数据不是一个对象或者是 Vnode 的实例,满足两者中的一个就直接返回,不需要响应式。如果这个两个条件都不满足的话,它会创建一个 ob,ob 一般就是 Observer 的实例,然后判断传进来的对象是否有 ob 这个属性或者是否为 Observer 的一个实例,是的话就赋值给 ob;不是的话,还会有一些判断条件,比较核心的就是判断它是否是数据或者是 Vue 的实例,如果不是的话,就创建一个 Observer 对象。

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()
    // 初始化实例的 vmCount 为0
    this.vmCount = 0
    // 将实例挂载到观察对象的 __ob__ 属性
    def(value, '__ob__', this)
    // 数组的响应式处理
    if (Array
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值