Vue2.0响应式原理源码解析

这里写目录标题

响应式原理

Vue应用的主要核心,是ViewModel驱动视图,数据驱动ViewModel的原理。其主要是应用了发布者-订阅者模式。通过对重新定义数据的getter,setter方法,并进行依赖收集,数据更新时通知依赖组件更新视图。

回到源码上。Vue的初始化有一套生命周期的过程,响应式的主要操作则是在执行beforeCreate之后。在初始化一系列的数据中执行了initData()方法。

源码解析

不同版本的vue的源码实现可能会有些不同,我这里的版本是2.6.14
在这里插入图片描述
首先我们要知道定义响应式是在哪个时间段实现的,从源码中我们可以看到,是在执行beforeCreate生命周期函数之后,Created之前。也就是说,这也就是我们在beforeCreate无法拿到Data中的数据的原因。
在这里插入图片描述

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方法里会对组件的props, methods, data,computed, watch等进行编译初始化

function initData (vm: Component) {
    
    
  let data = vm.$options.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
  const keys = Object.keys(data)
  const props = vm.$options.props
  const methods = vm.$options.methods
  let i = keys.length
  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) //将data上的属性代理到vm实例上。
    }
  }
  // observe data
  observe(data, true /* asRootData */)
}

initData()会先获取组件内部的data数据,然后判断data里的数据和props,或者和methods里的名称重复,则抛出错误提示,然后就会去监听data,执行observe方法

/**
 * Attempt to create an observer instance for a value,
 * returns the new observer if successfully observed,
 * or the existing observer if the value already has one.
 */
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)
  }
  if (asRootData && ob) {
    
    
    ob.vmCount++
  }
  return ob
}


在这里,会先判断data中的数据是否是对象,然后判断data中是否已经有了ob(也就是Observer实例)最后判断是否满足监听的条件。才会创建一个新的Observer对象

/**
 * Observer class that is attached to each observed
 * object. Once attached, the observer converts the target
 * object's property keys into getter/setters that
 * collect dependencies and dispatch updates.
 */
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<any>) {
    
    
    for (let i = 0, l = items.length; i < l; i++) {
    
    
      observe(items[i])
    }
  }
}

每一个observer实例都有自己的一个Dep, 在new Oberver后,会判断传入的value也就是vm.data是不是数组。

  1. 如果是数组,会采用函数劫持的方法重写数组的方法,先判断数组支不支持原型链,支持就将当前数组的原型指向已经重写了Array里的7种方法的arrayMethod,当数组里的方法被调用时,Dep会notify通知视图更新,然后执行ObserveArray方法,如果数组里的数据是对象,则继续回调observe();
  2. 如果是对象,则调用this.walk(),在walk()中,会遍历data的属性执行defineReactive()定义响应式
/**
 * Define a reactive property on an Object.
 */
export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
    
    
  const dep = new Dep()

  const property = Object.getOwnPropertyDescriptor(obj, key)
  if (property && property.configurable === false) {
    
    
    return
  }

  // cater for pre-defined getter/setters
  const getter = property && property.get
  const setter = property && property.set
  if ((!getter || setter) && arguments.length === 2) {
    
    
    val = obj[key]
  }

  let childOb = !shallow && observe(val)
  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
    },
    set: function reactiveSetter (newVal) {
    
    
      const 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()
      }
      // #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()
    }
  })
}

使用的Object.defineProperty()重新定义对象,给data的每个属性都添加了getter和setter方法。
在get的时候会调用dep.depend;如果是数组,则调用dependArray(对数组里的每个元素进行递归调用dep.depend);
在set的时候会先判断数据是否更新,未更新不做操作,更新则observe(),且dep.notify()
以下是Dep的代码,我们可以将Dep看作一个观察者。

/* @flow */

import type Watcher from './watcher'
import {
    
     remove } from '../util/index'
import config from '../config'

let uid = 0

/**
 * A dep is an observable that can have multiple
 * directives subscribing to it.
 */
export default class Dep {
    
    
  static target: ?Watcher;
  id: number;
  subs: Array<Watcher>;

  constructor () {
    
    
    this.id = uid++
    this.subs = [] //存储所有订阅的Watcher
  }

  addSub (sub: Watcher) {
    
    
    this.subs.push(sub)
  }

  removeSub (sub: Watcher) {
    
    
    remove(this.subs, sub)
  }

  depend () {
    
    
    if (Dep.target) {
    
    
      Dep.target.addDep(this)
    }
  }

  notify () {
    
    
    // stabilize the subscriber list first
    const subs = this.subs.slice()
    if (process.env.NODE_ENV !== 'production' && !config.async) {
    
    
      // subs aren't sorted in scheduler if not running async
      // we need to sort them now to make sure they fire in correct
      // order
      subs.sort((a, b) => a.id - b.id)
    }
    for (let i = 0, l = subs.length; i < l; i++) {
    
    
      subs[i].update()
    }
  }
}

// The current target watcher being evaluated.
// This is globally unique because only one watcher
// can be evaluated at a time.
Dep.target = null
const targetStack = []

export function pushTarget (target: ?Watcher) {
    
    
  targetStack.push(target)
  Dep.target = target
}

export function popTarget () {
    
    
  targetStack.pop()
  Dep.target = targetStack[targetStack.length - 1]
}

depend方法就是将当前dep的实例添加到对应的Watcher中,
notify方法就是通知所有收集的Wacher进行更新,subs[i].update()

至此对Vue的响应式有了简单的了解

猜你喜欢

转载自blog.csdn.net/qq_41028148/article/details/122944775
今日推荐