Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: handle fallthrough attributes in built-in components consistently #11140

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 57 additions & 4 deletions packages/runtime-core/src/components/Suspense.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
Comment,
type VNode,
type VNodeProps,
cloneVNode,
closeBlock,
createVNode,
currentBlock,
Expand All @@ -10,7 +11,14 @@ import {
normalizeVNode,
openBlock,
} from '../vnode'
import { ShapeFlags, isArray, isFunction, toNumber } from '@vue/shared'
import {
EMPTY_OBJ,
ShapeFlags,
extend,
isArray,
isFunction,
toNumber,
} from '@vue/shared'
import { type ComponentInternalInstance, handleSetupResult } from '../component'
import type { Slots } from '../componentSlots'
import {
Expand All @@ -31,6 +39,7 @@ import {
} from '../warning'
import { ErrorCodes, handleError } from '../errorHandling'
import { NULL_DYNAMIC_COMPONENT } from '../helpers/resolveAssets'
import type { Writable } from '../../types/util'

export interface SuspenseProps {
onResolve?: () => void
Expand All @@ -45,6 +54,14 @@ export interface SuspenseProps {
suspensible?: boolean
}

const ComponentProps = [
'onFallback',
'onPending',
'onResolve',
'timeout',
'suspensible',
]

export const isSuspense = (type: any): boolean => type.__isSuspense

// incrementing unique id for every pending branch
Expand Down Expand Up @@ -820,11 +837,15 @@ function hydrateSuspense(
function normalizeSuspenseChildren(vnode: VNode) {
const { shapeFlag, children } = vnode
const isSlotChildren = shapeFlag & ShapeFlags.SLOTS_CHILDREN
vnode.ssContent = normalizeSuspenseSlot(
isSlotChildren ? (children as Slots).default : children,
const attrs = getFallthroughAttrs(vnode)
vnode.ssContent = cloneVNode(
normalizeSuspenseSlot(
isSlotChildren ? (children as Slots).default : children,
),
attrs,
)
vnode.ssFallback = isSlotChildren
? normalizeSuspenseSlot((children as Slots).fallback)
? cloneVNode(normalizeSuspenseSlot((children as Slots).fallback), attrs)
: createVNode(Comment)
}

Expand Down Expand Up @@ -902,3 +923,35 @@ function isVNodeSuspensible(vnode: VNode) {
const suspensible = vnode.props && vnode.props.suspensible
return suspensible != null && suspensible !== false
}

/**
* TODO: The standard renderer should be abstracted to also allow Suspense to work, so
* Suspense can work as a normal component (like Transition and KeepAlive), instead of a "fake" one,
* as it currently is. This is why in some places special handling for it exists. Having it working
* as an standard component means that there's no need to re-implement all the handling of fallthrough attributes.
* Hence, this is just a workaround to normalize the behaviour across built-in components.
* See: https://github.com/vuejs/rfcs/discussions/664
*
* Suspense can receive fallthrough attributes from props and attrs
* - From props: When they are specified directly in the Suspense component (they're filtered here)
* - From attrs: When Suspense is a child of another component
*/
function getFallthroughAttrs(n2: VNode) {
const props = n2.props ?? EMPTY_OBJ
const fallthrough: Writable<typeof props> = {}
for (const key in props) {
if (!ComponentProps.includes(key)) {
fallthrough[key] = props[key]
}
}

if (
n2.suspense &&
n2.suspense.parentComponent &&
n2.suspense.parentComponent.attrs
) {
return extend({}, fallthrough, n2.suspense.parentComponent.attrs)
}

return fallthrough
}
3 changes: 3 additions & 0 deletions packages/runtime-core/types/util.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type Writable<T> = {
-readonly [P in keyof T]: T[P]
}