读 vue 源码一 (为什么this.message能够访问data里面的message)

Posted zpxm

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了读 vue 源码一 (为什么this.message能够访问data里面的message)相关的知识,希望对你有一定的参考价值。

12月离职后,打算在年后再找工作了,最近陆陆续续的看了黄轶老师的vue源码解析,趁着还有几天过年时间记录一下。

目标:vue如何实现通过this.key,就能直接访问data,props,methods,里面的key。

源码:当我们在项目中new Vue实例后会执行Vue构造函数的_init方法,并执行一些混入

function Vue (options) {
  if (process.env.NODE_ENV !== ‘production‘ &&
    !(this instanceof Vue) 
  ) {
    warn(‘Vue is a constructor and should be called with the `new` keyword‘)
  }
  this._init(options)
}

initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)

_init方法定义在initMixin方法中,在Vue原型上定义了_init方法,其他逻辑暂时不做分析,这里我们看到 将Vue实例赋值给了vm,然后合并options,然后执行了一些初始化方法,最后挂载方法。

export function initMixin (Vue: Class<Component>) {
  Vue.prototype._init = function (options?: Object) {
    const vm: Component = this
    // a uid
    vm._uid = uid++
    // 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
      )
    }

    vm._self = vm
    initLifecycle(vm)
    initEvents(vm)
    initRender(vm)
    callHook(vm, ‘beforeCreate‘)
    initInjections(vm) // resolve injections before data/props
    initState(vm)
    initProvide(vm) // resolve provide after data/props
    callHook(vm, ‘created‘)
if (vm.$options.el) {
      vm.$mount(vm.$options.el)
    }
  }
}

我们能够通过this.key访问是在initState方法执行时,里面执行了initData方法

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)
  }
}

initData方法实现如下,首先将data赋值给vm._data,然后判断data,props,methods里是否重名,然后调用proxy方法,最后调用observe方法将其变成响应式对象,这里暂不分析

function initData (vm: Component) {
  let data = vm.$options.data
  data = vm._data = typeof data === ‘function‘
    ? getData(data, vm)
    : data || {}// 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)
    }
  }
  // observe data
  observe(data, true /* asRootData */)
}

proxy方法实现就是更改defineProperty里的属性,将其的getter和setter,代理到sourceKey上,也就是我们上面说的vm._data上,然后我们就可以通过this.key访问data,props,message里面的key了

const sharedPropertyDefinition = {
  enumerable: true,
  configurable: true,
  get: noop,
  set: noop
}

export function proxy (target: Object, sourceKey: string, key: string) {
  sharedPropertyDefinition.get = function proxyGetter () {
    return this[sourceKey][key]
  }
  sharedPropertyDefinition.set = function proxySetter (val) {
    this[sourceKey][key] = val
  }
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

 

以上是关于读 vue 源码一 (为什么this.message能够访问data里面的message)的主要内容,如果未能解决你的问题,请参考以下文章

作为一个小白如何读vue的源码?

作为一个小白如何读vue的源码?

Vue源码思考(一门外观望,屋里这么热闹弄啥哩)

读Vue源码二 (响应式对象)

读Vue源码 (依赖收集与派发更新)

【面试题解析】从 Vue 源码分析 key 的作用