1. function initProps (vm: Component, propsOptions: Object) {
  2. // 根组件可能存在的 propsData 数据
  3. const propsData = vm.$options.propsData || {}
  4. // 普通组件使用方式时传递的 props 数据
  5. const props = vm._props = {}
  6. // cache prop keys so that future props updates can iterate using Array
  7. // instead of dynamic object key enumeration.
  8. // props 的 key 值集合
  9. const keys = vm.$options._propKeys = []
  10. // 当前实例是否是根实例
  11. const isRoot = !vm.$parent
  12. // root instance props should be converted
  13. // 如果是不是根实例,则取消响应式更新
  14. if (!isRoot) {
  15. toggleObserving(false)
  16. }
  17. // 遍历定义的 props 选项
  18. for (const key in propsOptions) {
  19. keys.push(key)
  20. // 用来校验名字(key)给定的 prop 数据是否符合预期的类型,并返回相应 prop 的值(或默认值)。
  21. const value = validateProp(key, propsOptions, propsData, vm)
  22. /* istanbul ignore else */
  23. // 非生产环境时的一些校验报错信息,更有助于开发者 debug
  24. if (process.env.NODE_ENV !== 'production') {
  25. // 如果相关 key 值以及预设,则抛出错误
  26. const hyphenatedKey = hyphenate(key)
  27. if (isReservedAttribute(hyphenatedKey) ||
  28. config.isReservedAttr(hyphenatedKey)) {
  29. warn(
  30. `"${hyphenatedKey}" is a reserved attribute and cannot be used as component prop.`,
  31. vm
  32. )
  33. }
  34. // 如果试图更新 props 值则抛出错误
  35. defineReactive(props, key, value, () => {
  36. if (!isRoot && !isUpdatingChildComponent) {
  37. warn(
  38. `Avoid mutating a prop directly since the value will be ` +
  39. `overwritten whenever the parent component re-renders. ` +
  40. `Instead, use a data or computed property based on the prop's ` +
  41. `value. Prop being mutated: "${key}"`,
  42. vm
  43. )
  44. }
  45. })
  46. } else {
  47. // 简单地使 props 具备响应式(非深度)
  48. defineReactive(props, key, value)
  49. }
  50. // static props are already proxied on the component's prototype
  51. // during Vue.extend(). We only need to proxy props defined at
  52. // instantiation here.
  53. // 代理访问那些未定义的 props
  54. if (!(key in vm)) {
  55. proxy(vm, `_props`, key)
  56. }
  57. }
  58. toggleObserving(true)
  59. }

validateProp

  1. export function validateProp (
  2. key: string,
  3. propOptions: Object,
  4. propsData: Object,
  5. vm?: Component
  6. ): any {
  7. // 取出 prop 配置
  8. const prop = propOptions[key]
  9. // 当前 prop 是否为传递
  10. const absent = !hasOwn(propsData, key)
  11. // 取出对应的值
  12. let value = propsData[key]
  13. // 判断当前 prop 类型是否有 boolean 类型
  14. const booleanIndex = getTypeIndex(Boolean, prop.type)
  15. // 如果是或者有 boolean 类型
  16. if (booleanIndex > -1) {
  17. if (absent && !hasOwn(prop, 'default')) {
  18. // 未传值且没有设置默认值则设置成 false
  19. value = false
  20. } else if (value === '' || value === hyphenate(key)) {
  21. // 当 prop 是一个空字符串,或者是一个名字由驼峰转连字符后与值为相同字符串的 prop
  22. // 并且 boolean 的权重高于 string,则设置成 true
  23. const stringIndex = getTypeIndex(String, prop.type)
  24. if (stringIndex < 0 || booleanIndex < stringIndex) {
  25. value = true
  26. }
  27. }
  28. }
  29. // 如果值为 undefined
  30. if (value === undefined) {
  31. // 获取默认值
  32. value = getPropDefaultValue(vm, prop, key)
  33. // 将默认值变成响应式数据
  34. const prevShouldObserve = shouldObserve
  35. toggleObserving(true)
  36. observe(value)
  37. toggleObserving(prevShouldObserve)
  38. }
  39. // props 的验证抛错
  40. if (
  41. process.env.NODE_ENV !== 'production' &&
  42. // skip validation for weex recycle-list child component props
  43. !(__WEEX__ && isObject(value) && ('@binding' in value))
  44. ) {
  45. assertProp(prop, key, value, vm, absent)
  46. }
  47. return value
  48. }