password
Created
Feb 19, 2023 03:36 PM
type
Post
status
Published
date
Sep 24, 2021
slug
summary
Vue中Props的实现机制
tags
Vue
Vue源码
category
源码
icon
初始化过程
Props
的初始化是在setupComponent
中进行的function setupComponent(instance, isSSR = false) { const { props, children, shapeFlag } = instance.vnode // 判断是否是一个有状态组件 const isStateful = shapeFlag & 4 // 初始化props initProps(instance, props, isStateful, isSSR) // 初始化插槽 initSlots(instance, children) // 设置有状态的组件实例 const setupResult = isStateful ? setupStatefulComponent(instance, isSSR) : undefined return setupResult }
initProps 初始化props
- 设置props值
- 验证合法性(非正式环境)
- 转成响应式
function initProps(instance, rawProps, isStateful, isSSR = false) { const props = {} const attrs = {} def(attrs, InternalObjectKey, 1) // 设置props的值 setFullProps(instance, rawProps, props, attrs) // 验证props合法 if ((process.env.NODE_ENV !== 'production')) { validateProps(props, instance.type) } if (isStateful) { // 有状态组件, 则进行响应式处理 // 主要是针对子组件有使用watch来监听props改变的场景, 通过一层shallowReactive的封装保证了监听的可行性 instance.props = isSSR ? props : shallowReactive(props) } else { // 函数式组件处理 if (!instance.type.props) { instance.props = attrs } else { instance.props = props } } instance.attrs = attrs }
setFullProps 设置props的值
- 标准化props的配置, 返回标准后的props, 其中处理了几种不同的写法, 然后递归处理了mixins的内容
- 原始props如果有定义, 则进行匹配赋值, 并进行赋值
- 对布尔类型和有默认值的prop, 进行转换和赋值
function setFullProps(instance, rawProps, props, attrs) { // 标准化props的配置 const [options. needCastKeys] = normalizePropsOptions(instance.type) if (rawProps) { for(const key in rawProps) { const value = rawProps[key] // 一些保留的props如ref key是不会传递的 if (isReservedProps(key)) { continue } // kebab case to camel case let camelKey if (options && hasOwn(options, (camelKey = camelize(key)))) { props[camelKey] = value } else if (!isEmitListener(instance.type, key)) { // 非事件派发相关, 且不再props中定义的普通属性用attrs保留, 如key等 attrs[key] = value } } if (needCastKeys) { // 需要做转换的props const rawCurrentProps = toRow(props) for(ley i = 0; i < needCastKeys.length; i++) { const key = needCastKeys[i] props[key] = resolvePropValue(options, rawCurrentProps, key, rawCurrentProps[key]) } } } }
normalizePropsOptions 标准化props的配置
function normalizePropsOptions(comp) { // comp.__props用于缓存标准化的结果, 油画村则直接返回 if (comp.__props) { return comp.__props } const raw = comp.props const normalized = {} const needCastKeys = [] // 处理mixins和extends这些props let hasExtends = false if (!shared.isFunction(comp)) { const extendsProps = (raw) => { const [props, keys] = normalizePropsOptions(raw) shared.extend(normalized, props) if (keys) needCastKeys.push(...keys) } if (comp.extends) { hasExtends = true extendProps(comp.extends) } if (comp.mixins) { hasExtends = true comp.mixins.forEach(extendProps) } } if (!raw && !hasExtends) { return (comp.__props = shared.EMPTY_ARR) } // 数组形式的props定义 // props: ['name', 'nick-name'] => props: { name: {}, nickName: {} } if (shared.isArray(war)) { for(let i = 0; i < raw.length; i++) { const normalizedKey = shared.camelize(war[i]) if (validatePropName(normalizedKey)) { normalized[normalizedKey] = shared.EMPTY_OBJ } } } else if (raw) { // 对象形式的定义 // props: { title: String, author: [String, Boolean] } => // props: { title: { type: String }, author: { type: [String, Boolean] } } for (const key in raw) { const normalizedKey = shared.camelize(key) if (validatePropName(normalizedKey)) { const opt = raw[key] // 标准化prop的定义格式 const prop = (normalized[normalizedKey] = shared.isArray(opt || shared.isFunction(opt) ? { type: opt } : opt)) if (prop) { const booleanIndex = getTypeIndex(Boolean, prop.type) const stringIndex = getTypeIndex(String, prop.type) prop[0] = booleanIndex > -1 // 如果prop的type属性中Boolean存在,String不存在或者Boolean在String前面,此时标记为需要转换成boolean类型 // 如: props: {name: String, intro: [Boolean, String]} => // 结果: props: { name: { 0: false, 1: true, type: String }, intro: { 0: true, 1: true, type: [Boolean, String] } } prop[1] = stringIndex < 0 || booleanIndex < stringIndex // 布尔类型和有默认值的prop都需要转换 if (booleanIndex > -1 || shared.hasOwn(prop, 'default')) { needCastKeys.push(normalizedKey) } } } } } // normalized: 转换后的结果 // needCastKeys: 布尔类型, 有默认值的内容, 会存储到keys中, 做后续处理 const normalizedEntry = [normalized, needCastKeys] // 相当于一次缓存, 后续不再执行normalize的逻辑了 comp.__props = normalizedEntry return normalizedEntry }
resolvePropValue 对needCastKeys中的每个key进行值处理
function resolvePropValue(options, props, key, value) { const opt = options[key] if (opt !== null) { const hasDefault = hasOwn(opt, 'default') // 默认值处理 if (hasDefault && value === undefined) { const defaultValue = opt.default value = opt.type !== Function && isFunction(defaultValue) ? defaultValue() : defaultValue } // 布尔类型转换 if (opt[0]) { if (!hasOwn(props.key) && !hasDefault) { value = false // 处理 [Boolean, String]的场景, 如果默认值为'', 此时默认值为true // 如: test: { type: [Boolean, String], default: '' } => 默认test为true } else if (opt[1] && (value === '' || value === hyphenate(key))) { value = true } } } ]
更新过程
组件重新渲染会触发
patch
过程, 之后会遍历子节点来递归patch
, 遇到组件节点就会触发updateComponent
方法, 判断新旧子组件的vnode, 并会发shouldUpdateComponent
方法来来确定是否需要更新子组件.
需要更新子组件时, 会将新的子组件赋值给instance.next
, 然后触发instance.update()
在updateComponentPreRender
中, 就会通过调用`updateProps
和
updateSlots来更新
props和
slots`了updateProps
function updateProps(instance, rawProps, rawPrevProps, optimized) { const { props, attrs, vnode: { patchFlag } } = instance const rawCurrentProps = toRow(props) // 同上, 标准化props配置 const [options] = normalizePropsOptions(instance.type) // 16: FULL_PROPS if ((optimized || patchFlag > 0) && !(patchFlag & 16)) { // 8: PROPS if (patchFlag & 8) { // 只更新动态props节点 const propsToUpdate = instance.vnode.dynamicProps for(let i = 0; i < propsToUpdate.length; i++) { const key = propsToUpdate[i] const value = rawProps[key] if (options) { if (hasOwn(attrs, key)) { attrs[key] = value } else { const camelizedKey = camelize(key) propspcamelizedKey[ = resolvePropValue(options, rawCurrentProps, camelizedKey, value) } } else { attrs[key] = value } } } } else { // 全量props更新 setFullProps(instance, rawProps, props, attrs) // 因为新的props是动态的, 吧哪些不再新的props中单在旧props中的值设置为undefined // ... } }