props属性一般是用在子实例上,但是Vue也允许应用到根实例上。与props配合使用的是propsData属性,这个属性似乎比较陌生,因为主要是Vue内部使用的。子实例要获取父实例传下来的值,就要通过propsData。

// 初始化props
function initProps (vm: Component, propsOptions: Object) {
  const propsData = vm.$options.propsData || {}
  const props = vm._props = {}

  // 注释里说是为了更新props属性时能够通过数组快速迭代,才有这个_propKeys
  const keys = vm.$options._propKeys = []
  const isRoot = !vm.$parent

  // observerState.shouldConvert 是用来控制是否实例化Observer的
  // 对于子实例来说,如果某个props属性是对象/数组,对其进一步的observe是没有意义的
  // 对于根实例,一般而言没有props和propsData,有propsData的话需要观察
  observerState.shouldConvert = isRoot
  for (const key in propsOptions) {
    keys.push(key)

    // 验证propsData传下来的值是否合法
    // 必要时赋默认值
    const value = validateProp(key, propsOptions, propsData, vm)


    // 把props变成响应式的
    if (process.env.NODE_ENV !== 'production') {
      if (isReservedProp[key] || config.isReservedAttr(key)) {
        warn(
          `"${key}" is a reserved attribute and cannot be used as component prop.`,
          vm
        )
      }
      defineReactive(props, key, value, () => {
        if (vm.$parent && !observerState.isSettingProps) {
          warn(
            `Avoid mutating a prop directly since the value will be ` +
            `overwritten whenever the parent component re-renders. ` +
            `Instead, use a data or computed property based on the prop's ` +
            `value. Prop being mutated: "${key}"`,
            vm
          )
        }
      })
    } else {
      defineReactive(props, key, value)
    }

    // 类似于data,将props的每一个属性代理到实例上
    // 实现原理依然是Object.defineProperty 利用get set
    if (!(key in vm)) {
      proxy(vm, `_props`, key)
    }
  }
  // 后面要初始化的data需要观察,因此shouldConvert需要置为true
  observerState.shouldConvert = true
}

props初始化和data初始化非常类似,都包含把数据变为响应式的,代理到实例上,只是value来源不一样。props的value来自于父组件,相对data初始化来说就多一步校验value合法性。

// 校验props合法性,返回合法的props值
export function validateProp (
  key: string,
  propOptions: Object,
  propsData: Object,
  vm?: Component
): any {
  const prop = propOptions[key]
  const absent = !hasOwn(propsData, key)

  // 从propsData获取父组件传递下来的值
  let value = propsData[key]

  // 布尔属性特殊处理
  // 类似于HTML5的布尔属性,vue的布尔属性也是只要声明置为true
  if (isType(Boolean, prop.type)) {
    if (absent && !hasOwn(prop, 'default')) {
      value = false
    } else if (!isType(String, prop.type) && (value === '' || value === hyphenate(key))) {
      value = true
    }
  }
  // 父实例没有传递下来值,采用默认值
  if (value === undefined) {
    value = getPropDefaultValue(vm, prop, key)
    // 默认值是新值,需要被观察
    const prevShouldConvert = observerState.shouldConvert
    observerState.shouldConvert = true
    observe(value)
    observerState.shouldConvert = prevShouldConvert
  }

  // 这一块是负责required和validator两个属性
  if (process.env.NODE_ENV !== 'production') {
    assertProp(prop, key, value, vm, absent)
  }
  return value
}

results matching ""

    No results matching ""