refactor: make portal tree-shakeable

This commit is contained in:
Evan You
2020-02-15 11:40:09 -05:00
parent 0fc720c34a
commit 9d2ac6675a
14 changed files with 293 additions and 240 deletions

View File

@@ -72,9 +72,9 @@ const KeepAliveImpl = {
const sink = instance.sink as KeepAliveSink
const {
renderer: {
move,
unmount: _unmount,
options: { createElement }
m: move,
um: _unmount,
o: { createElement }
},
parentSuspense
} = sink

View File

@@ -0,0 +1,118 @@
import { ComponentInternalInstance } from '../component'
import { SuspenseBoundary } from './Suspense'
import { RendererInternals, MoveType } from '../renderer'
import { VNode, VNodeArrayChildren } from '../vnode'
import { isString, ShapeFlags, PatchFlags } from '@vue/shared'
import { warn } from '../warning'
export const isPortal = (type: any): boolean => type.__isPortal
export interface PortalProps {
target: string | object
}
export const PortalImpl = {
__isPortal: true,
process(
n1: VNode | null,
n2: VNode,
container: object,
anchor: object | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
isSVG: boolean,
optimized: boolean,
{
mc: mountChildren,
pc: patchChildren,
pbc: patchBlockChildren,
m: move,
c: insertComment,
o: { querySelector, setElementText }
}: RendererInternals
) {
const targetSelector = n2.props && n2.props.target
const { patchFlag, shapeFlag, children } = n2
if (n1 == null) {
if (__DEV__ && isString(targetSelector) && !querySelector) {
warn(
`Current renderer does not support string target for Portals. ` +
`(missing querySelector renderer option)`
)
}
const target = (n2.target = isString(targetSelector)
? querySelector!(targetSelector)
: targetSelector)
if (target != null) {
if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
setElementText(target, children as string)
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
mountChildren(
children as VNodeArrayChildren,
target,
null,
parentComponent,
parentSuspense,
isSVG,
optimized
)
}
} else if (__DEV__) {
warn('Invalid Portal target on mount:', target, `(${typeof target})`)
}
} else {
// update content
const target = (n2.target = n1.target)!
if (patchFlag === PatchFlags.TEXT) {
setElementText(target, children as string)
} else if (n2.dynamicChildren) {
// fast path when the portal happens to be a block root
patchBlockChildren(
n1.dynamicChildren!,
n2.dynamicChildren,
container,
parentComponent,
parentSuspense,
isSVG
)
} else if (!optimized) {
patchChildren(
n1,
n2,
target,
null,
parentComponent,
parentSuspense,
isSVG
)
}
// target changed
if (targetSelector !== (n1.props && n1.props.target)) {
const nextTarget = (n2.target = isString(targetSelector)
? querySelector!(targetSelector)
: targetSelector)
if (nextTarget != null) {
// move content
if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
setElementText(target, '')
setElementText(nextTarget, children as string)
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
for (let i = 0; i < (children as VNode[]).length; i++) {
move((children as VNode[])[i], nextTarget, null, MoveType.REORDER)
}
}
} else if (__DEV__) {
warn('Invalid Portal target on update:', target, `(${typeof target})`)
}
}
}
// insert an empty node as the placeholder for the portal
insertComment(n1, n2, container, anchor)
}
}
// Force-casted public typing for h and TSX props inference
export const Portal = (PortalImpl as any) as {
__isPortal: true
new (): { $props: PortalProps }
}

View File

@@ -13,6 +13,8 @@ export interface SuspenseProps {
onRecede?: () => void
}
export const isSuspense = (type: any): boolean => type.__isSuspense
// Suspense exposes a component-like API, and is treated like a component
// in the compiler, but internally it's a special built-in type that hooks
// directly into the renderer.
@@ -79,8 +81,8 @@ function mountSuspense(
rendererInternals: RendererInternals
) {
const {
patch,
options: { createElement }
p: patch,
o: { createElement }
} = rendererInternals
const hiddenContainer = createElement('div')
const suspense = (n2.suspense = createSuspenseBoundary(
@@ -138,7 +140,7 @@ function patchSuspense(
parentComponent: ComponentInternalInstance | null,
isSVG: boolean,
optimized: boolean,
{ patch }: RendererInternals
{ p: patch }: RendererInternals
) {
const suspense = (n2.suspense = n1.suspense)!
suspense.vnode = n2
@@ -236,11 +238,11 @@ function createSuspenseBoundary<HostNode, HostElement>(
rendererInternals: RendererInternals<HostNode, HostElement>
): SuspenseBoundary<HostNode, HostElement> {
const {
patch,
move,
unmount,
next,
options: { parentNode }
p: patch,
m: move,
um: unmount,
n: next,
o: { parentNode }
} = rendererInternals
const suspense: SuspenseBoundary<HostNode, HostElement> = {