feat: attribute fallthrough
This commit is contained in:
parent
6ce39b4d20
commit
cb01733842
@ -1,4 +1,4 @@
|
||||
import { EMPTY_OBJ, isReservedProp } from './utils'
|
||||
import { EMPTY_OBJ } from './utils'
|
||||
import { Component, ComponentClass, MountedComponent } from './component'
|
||||
import { immutable, unwrap, lock, unlock } from '@vue/observer'
|
||||
import {
|
||||
@ -38,29 +38,42 @@ export function updateProps(instance: MountedComponent, nextProps: Data) {
|
||||
// on every component vnode is guarunteed to be a fresh object.
|
||||
export function normalizeComponentProps(
|
||||
raw: any,
|
||||
options: ComponentPropsOptions,
|
||||
rawOptions: ComponentPropsOptions,
|
||||
Component: ComponentClass
|
||||
): Data {
|
||||
if (!raw) {
|
||||
const hasDeclaredProps = rawOptions !== void 0
|
||||
const options = (hasDeclaredProps &&
|
||||
normalizePropsOptions(rawOptions)) as NormalizedPropsOptions
|
||||
if (!raw && !hasDeclaredProps) {
|
||||
return EMPTY_OBJ
|
||||
}
|
||||
const res: Data = {}
|
||||
const normalizedOptions = options && normalizePropsOptions(options)
|
||||
for (const key in raw) {
|
||||
if (isReservedProp(key)) {
|
||||
continue
|
||||
}
|
||||
if (__DEV__ && normalizedOptions != null) {
|
||||
validateProp(key, raw[key], normalizedOptions[key], Component)
|
||||
} else {
|
||||
res[key] = raw[key]
|
||||
if (raw) {
|
||||
for (const key in raw) {
|
||||
if (key === 'key' || key === 'ref' || key === 'slot') {
|
||||
continue
|
||||
}
|
||||
if (hasDeclaredProps) {
|
||||
if (options.hasOwnProperty(key)) {
|
||||
if (__DEV__) {
|
||||
validateProp(key, raw[key], options[key], Component)
|
||||
}
|
||||
res[key] = raw[key]
|
||||
} else {
|
||||
// when props are explicitly declared, any non-matching prop is
|
||||
// extracted into attrs instead.
|
||||
;(res.attrs || (res.attrs = {}))[key] = raw[key]
|
||||
}
|
||||
} else {
|
||||
res[key] = raw[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
// set default values
|
||||
if (normalizedOptions != null) {
|
||||
for (const key in normalizedOptions) {
|
||||
if (hasDeclaredProps) {
|
||||
for (const key in options) {
|
||||
if (res[key] === void 0) {
|
||||
const opt = normalizedOptions[key]
|
||||
const opt = options[key]
|
||||
if (opt != null && opt.hasOwnProperty('default')) {
|
||||
const defaultValue = opt.default
|
||||
res[key] =
|
||||
|
@ -103,30 +103,31 @@ export function normalizeComponentRoot(
|
||||
componentVNode &&
|
||||
(flags & VNodeFlags.COMPONENT || flags & VNodeFlags.ELEMENT)
|
||||
) {
|
||||
const parentData = componentVNode.data || EMPTY_OBJ
|
||||
const childData = vnode.data || EMPTY_OBJ
|
||||
let extraData: any = null
|
||||
for (const key in parentData) {
|
||||
// class/style bindings on parentVNode are merged down to child
|
||||
// component root.
|
||||
if (key === 'class') {
|
||||
;(extraData || (extraData = {})).class = childData.class
|
||||
? [].concat(childData.class, parentData.class)
|
||||
: parentData.class
|
||||
} else if (key === 'style') {
|
||||
;(extraData || (extraData = {})).style = childData.style
|
||||
? [].concat(childData.style, parentData.style)
|
||||
: parentData.style
|
||||
} else if (key.startsWith('nativeOn')) {
|
||||
// nativeOn* handlers are merged down to child root as native listeners
|
||||
const event = 'on' + key.slice(8)
|
||||
;(extraData || (extraData = {}))[event] = childData.event
|
||||
? [].concat(childData.event, parentData[key])
|
||||
: parentData[key]
|
||||
const parentData = componentVNode.data
|
||||
if (parentData != null) {
|
||||
let extraData: any = null
|
||||
for (const key in parentData) {
|
||||
// attrs/class/style bindings on parentVNode are merged down to child
|
||||
// component root,
|
||||
// nativeOn* handlers are merged to child root as normal on* handlers.
|
||||
// cloneVNode contains special logic for merging these props with
|
||||
// existing values.
|
||||
if (key === 'attrs') {
|
||||
extraData = extraData || {}
|
||||
const { attrs } = parentData
|
||||
for (const attr in attrs) {
|
||||
extraData[attr] = attrs[attr]
|
||||
}
|
||||
} else if (key === 'class' || key === 'style') {
|
||||
;(extraData || (extraData = {}))[key] = parentData[key]
|
||||
} else if (key.startsWith('nativeOn')) {
|
||||
;(extraData || (extraData = {}))['on' + key.slice(8)] =
|
||||
parentData[key]
|
||||
}
|
||||
}
|
||||
if (extraData) {
|
||||
vnode = cloneVNode(vnode, extraData)
|
||||
}
|
||||
}
|
||||
if (extraData) {
|
||||
vnode = cloneVNode(vnode, extraData)
|
||||
}
|
||||
if (vnode.el) {
|
||||
vnode = cloneVNode(vnode)
|
||||
|
@ -258,7 +258,28 @@ export function cloneVNode(vnode: VNode, extraData?: VNodeData): VNode {
|
||||
}
|
||||
}
|
||||
for (const key in extraData) {
|
||||
clonedData[key] = extraData[key]
|
||||
const existing = clonedData[key]
|
||||
const extra = extraData[key]
|
||||
if (extra === void 0) {
|
||||
continue
|
||||
}
|
||||
// special merge behavior for attrs / class / style / on.
|
||||
let isOn
|
||||
if (key === 'attrs') {
|
||||
clonedData.attrs = existing
|
||||
? Object.assign({}, existing, extra)
|
||||
: extra
|
||||
} else if (
|
||||
key === 'class' ||
|
||||
key === 'style' ||
|
||||
(isOn = key.startsWith('on'))
|
||||
) {
|
||||
// all three props can handle array format, so we simply merge them
|
||||
// by concating.
|
||||
clonedData[key] = existing ? [].concat(existing, extra) : extra
|
||||
} else {
|
||||
clonedData[key] = extra
|
||||
}
|
||||
}
|
||||
}
|
||||
return createVNode(
|
||||
|
Loading…
Reference in New Issue
Block a user