【根据源码分析Vue 组件中 props的实现原理 】
根据源码分析Vue 组件中 props的实现原理
- 整体功能概述
- 代码详细解释
- 1. 导入模块和类型定义
- 2. `validateProp` 函数
- 3. `getPropDefaultValue` 函数
- 4. `assertProp` 函数
- 5. `assertType` 函数
- 6. 其他辅助函数
- 实现原理总结
整体功能概述
源码实现了一系列用于验证和处理组件 props
的工具函数,主要包括:
validateProp
:验证单个prop
的值,并处理默认值和类型转换。getPropDefaultValue
:获取prop
的默认值。assertProp
:断言prop
是否有效。assertType
:检查值的类型是否符合预期。- 其他辅助函数,如
getType
、isSameType
等。
代码详细解释
1. 导入模块和类型定义
/* @flow */
import { warn } from './debug'
import { observe, toggleObserving, shouldObserve } from '../observer/index'
import {
hasOwn,
isObject,
toRawType,
hyphenate,
capitalize,
isPlainObject
} from 'shared/util'
type PropOptions = {
type: Function | Array<Function> | null,
default: any,
required: ?boolean,
validator: ?Function
};
/* @flow */
:表示该文件使用 Flow 进行静态类型检查。- 导入了多个工具函数和模块,用于调试警告、响应式观察和一些通用工具方法。
PropOptions
定义了prop
的选项类型,包括type
(类型)、default
(默认值)、required
(是否必需)和validator
(自定义验证函数)。
2. validateProp
函数
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]
// boolean casting
const booleanIndex = getTypeIndex(Boolean, prop.type)
if (booleanIndex > -1) {
if (absent && !hasOwn(prop, 'default')) {
value = false
} else if (value === '' || value === hyphenate(key)) {
// only cast empty string / same name to boolean if
// boolean has higher priority
const stringIndex = getTypeIndex(String, prop.type)
if (stringIndex < 0 || booleanIndex < stringIndex) {
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 prevShouldObserve = shouldObserve
toggleObserving(true)
observe(value)
toggleObserving(prevShouldObserve)
}
if (
process.env.NODE_ENV !== 'production' &&
// skip validation for weex recycle-list child component props
!(__WEEX__ && isObject(value) && ('@binding' in value))
) {
assertProp(prop, key, value, vm, absent)
}
return value
}
- 功能:验证单个
prop
的值,并处理默认值和类型转换。 - 参数:
key
:prop
的名称。propOptions
:prop
的选项对象。propsData
:传入的props
数据。vm
:可选的 Vue 实例。
- 步骤:
- 获取
prop
的选项和值,并检查是否缺失。 - 处理布尔类型的
prop
,如果缺失且没有默认值,将其设为false
;如果值为空字符串或与prop
名称相同,且布尔类型优先级更高,则将其设为true
。 - 如果值为
undefined
,则获取默认值,并确保默认值是响应式的。 - 在开发环境下,调用
assertProp
函数进行prop
验证。 - 返回验证后的值。
- 获取
3. getPropDefaultValue
函数
function getPropDefaultValue (vm: ?Component, prop: PropOptions, key: string): any {
// no default, return undefined
if (!hasOwn(prop, 'default')) {
return undefined
}
const def = prop.default
// warn against non-factory defaults for Object & Array
if (process.env.NODE_ENV !== 'production' && isObject(def)) {
warn(
'Invalid default value for prop "' + key + '": ' +
'Props with type Object/Array must use a factory function ' +
'to return the default value.',
vm
)
}
// the raw prop value was also undefined from previous render,
// return previous default value to avoid unnecessary watcher trigger
if (vm && vm.$options.propsData &&
vm.$options.propsData[key] === undefined &&
vm._props[key] !== undefined
) {
return vm._props[key]
}
// call factory function for non-Function types
// a value is Function if its prototype is function even across different execution context
return typeof def === 'function' && getType(prop.type) !== 'Function'
? def.call(vm)
: def
}
- 功能:获取
prop
的默认值。 - 参数:
vm
:可选的 Vue 实例。prop
:prop
的选项对象。key
:prop
的名称。
- 步骤:
- 如果
prop
没有默认值,返回undefined
。 - 在开发环境下,如果默认值是对象或数组且不是工厂函数,发出警告。
- 如果上一次渲染时
prop
的值也是undefined
,返回上一次的默认值,以避免不必要的监听器触发。 - 如果默认值是函数且
prop
类型不是Function
,调用该函数并返回结果;否则,直接返回默认值。
- 如果
4. assertProp
函数
function assertProp (
prop: PropOptions,
name: string,
value: any,
vm: ?Component,
absent: boolean
) {
if (prop.required && absent) {
warn(
'Missing required prop: "' + name + '"',
vm
)
return
}
if (value == null && !prop.required) {
return
}
let type = prop.type
let valid = !type || type === true
const expectedTypes = []
if (type) {
if (!Array.isArray(type)) {
type = [type]
}
for (let i = 0; i < type.length && !valid; i++) {
const assertedType = assertType(value, type[i])
expectedTypes.push(assertedType.expectedType || '')
valid = assertedType.valid
}
}
if (!valid) {
warn(
getInvalidTypeMessage(name, value, expectedTypes),
vm
)
return
}
const validator = prop.validator
if (validator) {
if (!validator(value)) {
warn(
'Invalid prop: custom validator check failed for prop "' + name + '".',
vm
)
}
}
}
- 功能:断言
prop
是否有效。 - 参数:
prop
:prop
的选项对象。name
:prop
的名称。value
:prop
的值。vm
:可选的 Vue 实例。absent
:表示prop
是否缺失。
- 步骤:
- 如果
prop
是必需的但缺失,发出警告并返回。 - 如果
prop
不是必需的且值为null
或undefined
,直接返回。 - 检查
prop
的类型是否符合预期,如果不符合,发出警告。 - 如果
prop
有自定义验证函数,调用该函数进行验证,如果验证失败,发出警告。
- 如果
5. assertType
函数
const simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/
function assertType (value: any, type: Function): {
valid: boolean;
expectedType: string;
} {
let valid
const expectedType = getType(type)
if (simpleCheckRE.test(expectedType)) {
const t = typeof value
valid = t === expectedType.toLowerCase()
// for primitive wrapper objects
if (!valid && t === 'object') {
valid = value instanceof type
}
} else if (expectedType === 'Object') {
valid = isPlainObject(value)
} else if (expectedType === 'Array') {
valid = Array.isArray(value)
} else {
valid = value instanceof type
}
return {
valid,
expectedType
}
}
- 功能:检查值的类型是否符合预期。
- 参数:
value
:要检查的值。type
:预期的类型。
- 步骤:
- 获取预期类型的名称。
- 如果预期类型是简单类型(
String
、Number
、Boolean
、Function
、Symbol
),检查值的类型是否匹配;如果不匹配且值是对象,检查是否是该类型的实例。 - 如果预期类型是
Object
,检查值是否是普通对象。 - 如果预期类型是
Array
,检查值是否是数组。 - 对于其他类型,检查值是否是该类型的实例。
- 返回检查结果和预期类型名称。
6. 其他辅助函数
function getType (fn) {
const match = fn && fn.toString().match(/^\s*function (\w+)/)
return match ? match[1] : ''
}
function isSameType (a, b) {
return getType(a) === getType(b)
}
function getTypeIndex (type, expectedTypes): number {
if (!Array.isArray(expectedTypes)) {
return isSameType(expectedTypes, type) ? 0 : -1
}
for (let i = 0, len = expectedTypes.length; i < len; i++) {
if (isSameType(expectedTypes[i], type)) {
return i
}
}
return -1
}
function getInvalidTypeMessage (name, value, expectedTypes) {
let message = `Invalid prop: type check failed for prop "${name}".` +
` Expected ${expectedTypes.map(capitalize).join(', ')}`
const expectedType = expectedTypes[0]
const receivedType = toRawType(value)
const expectedValue = styleValue(value, expectedType)
const receivedValue = styleValue(value, receivedType)
// check if we need to specify expected value
if (expectedTypes.length === 1 &&
isExplicable(expectedType) &&
!isBoolean(expectedType, receivedType)) {
message += ` with value ${expectedValue}`
}
message += `, got ${receivedType} `
// check if we need to specify received value
if (isExplicable(receivedType)) {
message += `with value ${receivedValue}.`
}
return message
}
function styleValue (value, type) {
if (type === 'String') {
return `"${value}"`
} else if (type === 'Number') {
return `${Number(value)}`
} else {
return `${value}`
}
}
function isExplicable (value) {
const explicitTypes = ['string', 'number', 'boolean']
return explicitTypes.some(elem => value.toLowerCase() === elem)
}
function isBoolean (...args) {
return args.some(elem => elem.toLowerCase() === 'boolean')
}
getType
:通过函数的字符串表示获取类型名称。isSameType
:检查两个值的类型是否相同。getTypeIndex
:获取类型在预期类型数组中的索引。getInvalidTypeMessage
:生成类型检查失败的错误信息。styleValue
:根据类型格式化值。isExplicable
:检查值的类型是否可明确表示。isBoolean
:检查值的类型是否为布尔类型。
实现原理总结
- 验证流程 :当组件接收到
props
时,validateProp
函数会对每个prop
进行验证。首先进行布尔类型转换,然后处理默认值,最后在非生产环境下进行验证。 - 类型检查 :
assertType
函数负责检查值的类型是否符合要求,根据不同的类型使用不同的检查方法。 - 默认值处理 :
getPropDefaultValue
函数负责获取prop
的默认值,并处理非工厂函数的默认值警告。 - 自定义验证 :如果
prop
定义了自定义验证函数,则在assertProp
函数中调用该函数进行验证。
通过这些函数的协作,Vue
实现了对 props
的验证和处理,确保组件接收到的 props
符合定义要求。