Vue 源码阅读(二)props 属性实现细节

起因

偶然看到一个有趣的 issue

出于好奇,重新整理了一份测试代码

小结

从结果上来看,给组件设置 props 属性时,如果 type 属性值

  • Boolean 类型,如果没有设置 default 属性,那么默认返回 false
  • String 类型,如果没有设置 default 属性,那么默认返回 undefined
  • 其它类型,同理 String

官方文档 components.html#Prop-Validation

源码

src/core/instance/state.js


function initProps (vm: Component, propsOptions: Object) {
  const propsData = vm.$options.propsData || {}
  const props = vm._props = {}
  // cache prop keys so that future props updates can iterate using Array
  // instead of dynamic object key enumeration.
  const keys = vm.$options._propKeys = []
  const isRoot = !vm.$parent
  // root instance props should be converted
  observerState.shouldConvert = isRoot
  for (const key in propsOptions) {
    keys.push(key)
    const value = validateProp(key, propsOptions, propsData, vm)   // ========= 关键位置
    /* istanbul ignore else */
    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)
    }
    // static props are already proxied on the component's prototype
    // during Vue.extend(). We only need to proxy props defined at
    // instantiation here.
    if (!(key in vm)) {
      proxy(vm, `_props`, key)
    }
  }
  observerState.shouldConvert = true
}

src/core/util/props.js

export function validateProp (
  key: string,
  propOptions: Object,
  propsData: Object,
  vm?: Component
): any {
  const prop = propOptions[key]
  const absent = !hasOwn(propsData, key)
  let value = propsData[key]
  // handle boolean props
  if (isType(Boolean, prop.type)) {    // ======= 关键代码
    if (
        absent && 
        !hasOwn(prop, 'default')      // 如果不存在 defalut 属性
    ) {   
      value = false
    } else if (
        !isType(String, prop.type) &&     // 疑问1:这里是否会多此一举,上一层判断已经得出结果
        (
            value === '' ||               // 如果传入的值是空字符串,潜意识中 js 的类型转换会转成 false,容易误导
            value === hyphenate(key)      // 如果传入的值经过 `kebab-case` 转换后和 key 相等,不知道什么场景下会这么用
        )
      ) {
      value = true
    }
  }
  // check default value
  if (value === undefined) {
    value = getPropDefaultValue(vm, prop, key)
    // since the default value is a fresh copy,
    // make sure to observe it.
    const prevShouldConvert = observerState.shouldConvert
    observerState.shouldConvert = true
    observe(value)
    observerState.shouldConvert = prevShouldConvert
  }
  if (process.env.NODE_ENV !== 'production') {
    assertProp(prop, key, value, vm, absent)
  }
  return value
}

自我解疑

疑问1

src/core/util/props.js

/**
 * Use function string name to check built-in types,
 * because a simple equality check will fail when running
 * across different vms / iframes.
 */
function getType (fn) {
  const match = fn && fn.toString().match(/^\s*function (\w+)/)
  return match ? match[1] : ''
}

function isType (type, fn) {
  if (!Array.isArray(fn)) {  // 关键代码:prop.type 可以接收一个数组,例如 [Boolean, String]
    return getType(fn) === getType(type)
  }
  for (let i = 0, len = fn.length; i < len; i++) {
    if (getType(fn[i]) === getType(type)) {
      return true
    }
  }
  /* istanbul ignore next */
  return false
}
    原文作者:siwuxie
    原文地址: https://segmentfault.com/a/1190000009052365
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞