Vue源码学习2-props&methods&data

  • 内容
  • 评论
  • 相关

前言

---props是父子组件之间通信的方式之一

--data会在Vue中被处理成响应式数据

--以下vm都代指vue的实例

--以下所有代码来自Vue 2.6.11 版本

initState

在new Vue 的整个流程中,props和data数据的处理都发生在 _init 的 initState 方法中(newVue整体流程可以看这里

进入这个方法 (src/core/instance/state.js)可以清楚的看到处理流程

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

根Vue实例中的data可以直接返回一个对象 ,组件就必须返回一个匿名函数(原因就不说了,熟练Vue的应该都知道)

//根Vue实例
new Vue({
  data:{}
});
//组件实例
component = {
  data(){
    return {}
  }
}

initProps

props的初始化赋值发送在 组件化的流程中,这里就不说了,后面梳理组件化流程的时候再说

平时开发使用 props 的方式有两种:

1:数组形式  props:['name','person']

2:对象形式

props:{
  type:{
    type:String,
    default(){},
    ......更多选项看vue的文档就行了
  }
}

下面这段代码是 initProps 方法的核心处理流程,忽略掉环境判断以及一些warn,总的来说就是遍历一个对象,然后 validateProp(校验/默认值/取值 等操作) ,最后使用 defineReactive 把数据处理成响应式数据(具体的响应式处理流程后面再说)

Vue通过proxy方法把props中的所有数据都挂载到 vm 的 _props 这个属性上,然后,将vm.xxx 的引用代理到 vm._props.xxx上,当我们访问 this.name 的时候,就相当于访问 this._props.name

proxy在 data methods computed 初始化的过程中都会见到

for (const key in propsOptions) {
  keys.push(key)
  const value = validateProp(key, propsOptions, propsData, vm)
  /* istanbul ignore else */
  if (process.env.NODE_ENV !== 'production') {
    const hyphenatedKey = hyphenate(key)
    if (isReservedAttribute(hyphenatedKey) ||
        config.isReservedAttr(hyphenatedKey)) {
      warn(
        ....,vm
      )
    }
    defineReactive(props, key, value, () => {
      if (!isRoot && !isUpdatingChildComponent) {
        warn(
          ......,vm
        )
      }
    })
  } else {
    defineReactive(props, key, value)
  }
  if (!(key in vm)) {
    proxy(vm, `_props`, key)
  }
}

proxy

const sharedPropertyDefinition = {
  enumerable: true,
  configurable: true,
  get: noop,
  set: noop
}
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)
}

initData

initData的流程和props略有不同,先是检查一下key是否和props和methods有重复,然后就是和initProps一样的proxy操作,只不过这次是 vm._data

最后,observe就是处理响应式数据的流程,这里还是先不说,后面再梳理

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)
  }
}
// observe data
observe(data, true /* asRootData */)

initMethods

initMethods方法做的就相当简单了,简单的遍历操作,先是校验,最后就是bind(这里的bind方法来自于 share/util.js),就等价于Function.prototype.bind方法,将 method 的作用域绑定为 vm,并且给 vm[key]赋值  vm[key] = method。然后我们就能愉快的在代码中调用this.xxxx方法了(这里的bind、作用域两个知识点不懂就先去自行了解)

function initMethods (vm: Component, methods: Object) {
  const props = vm.$options.props
  for (const key in methods) {
    ......忽略一段代码
    1:检查是否为 function
    2:检查和props是否 key 冲突
    .......
    vm[key] = typeof methods[key] !== 'function' ? noop : bind(methods[key], vm)
  }
}

总结

以上就简单的梳理了一下代码,细节方面就放在后面的响应式原理来讲。下篇文章不打算讲 initComputed 和  initWatch,必须要先讲清楚Vue的响应式原理才能更容易学习和理解这两个东西。

评论

0条评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注