当前位置:   article > 正文

源码分析vue响应式原理_vue dep.js

vue dep.js

源码分析vue响应式原理

经过上篇简单实现vue响应式原理如果打不开可以看这个)之后,
我们已经简略地了解了响应式是怎么通过发布-订阅模式来实现的,这篇主要从源码分析Vue对响应式原理的实现。

找到initData

在vue源码入口文件vue/src/core/index.js中,可以看到import Vue from './instance/index',导入了Vue这个对象。

vue/src/core/instance/index.js中,

import {
    initMixin } from './init'
//...

function Vue (options) {
   
  //...
  this._init(options)
}

initMixin(Vue)
// ...

export default Vue

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

可以看到Vue是一个函数方法,调用该方法时会调用一个叫_init的初始化方法,并传入options参数,同时这个文件还执行了initMixin方法。

vue/src/core/instance/init.js中,

// ...
import {
    initState } from './state'
import {
    extend, mergeOptions, formatComponentName } from '../util/index'

export function initMixin (Vue: Class<Component>) {
   
  Vue.prototype._init = function (options?: Object) {
   
    const vm: Component = this

    // ...

    // merge options
    if (options && options._isComponent) {
   
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options)
    } else {
   
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {
   },
        vm
      )
    }
    // ...
    initState(vm)

    // ...
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

看到_init方法就是在initMixin方法中定义的,在_init方法中,声明了常量vm并赋值当前实例,接受了options并做了处理,还调用了initState方法。

本篇我们不对options做深入研究,但是要知道实例化Vue时传入的对象参数可以在这里取到。

vue/src/core/instance/state.js中,

import {
   
  set,
  del,
  observe,
  defineReactive,
  toggleObserving
} from '../observer/index'

export function initState (vm: Component) {
   
  // ...
  const opts = vm.$options
  // ...
  if (opts.data) {
   
    initData(vm)
  } else {
   
    observe(vm._data = {
   }, true /* asRootData */)
  }
  // ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

当实例化Vue,如

new Vue({
   
    el: '#app',
    data: {
   
        text: 'hello world',
    }
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

传入的data就是这里的opts.data,当它存在时,调用initData,否则调用observe方法,并初始化_data属性为空对象。

initData

observe方法我们后面会再讲,上面我们已经找到了initData方法,来看一下。

function initData (vm: Component) {
   
  let data = vm.$options.data
  data = vm._data = typeof data === 'function'
    ? getData(data, vm)
    : data || {
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

声明data对象,赋值为vm.$options.data。

当data为函数时调用getData(data, vm),结果赋值给data和vm._data。否则判断data是否存在,存在就将vm._data设置为data,不存在则讲data和vm._data都设为空对象。

export function getData (data: Function, vm: Component): any {
   
  // #7573 disable dep collection when invoking data getters
  pushTarget()
  try {
   
    return data.call(vm, vm)
  } catch (e) {
   
    handleError(e, vm, `data()`)
    return {
   }
  } finally {
   
    popTarget()
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

vue/src/core/observer/dep.js

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]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

可以发现实际上getData主要就是执行并返回了data.call(vm, vm),当data为函数时,将data方法的this指向当前vue实例,调用data方法并传入该vue实例。

接着往下

function initData (vm: Component) {
   
  // ...
  // 判断data是否为一个Object实例,如果不是,赋值为空对象,并在非生产环境报警告:data函数返回的值必须是一个object
  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
    )
  }
  // ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

vue/src/shared/util.js

export function isPlainObject (obj: any): boolean {
   
  return _toString.call(obj) === '[object Object]'
}
  • 1
  • 2
  • 3
  • 4

继续往下

function initData (vm: Component) {
   
  // ...
  // 获取属性名数组
  const keys = Object.keys(data)
  // 获取props
  const props = vm.$options.props
  // 获取methods
  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
        )
      }
    }
    // 非生产环境,props和属性名重名警告
    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)) {
   
      // 过滤 $ 或者 _开头的属性,将其余属性代理到实
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/283814
推荐阅读
相关标签
  

闽ICP备14008679号