feat(compiler-dom/runtime-dom): stringify eligible static trees
This commit is contained in:
@@ -85,7 +85,12 @@ export { toHandlers } from './helpers/toHandlers'
|
||||
export { renderSlot } from './helpers/renderSlot'
|
||||
export { createSlots } from './helpers/createSlots'
|
||||
export { pushScopeId, popScopeId, withScopeId } from './helpers/scopeId'
|
||||
export { setBlockTracking, createTextVNode, createCommentVNode } from './vnode'
|
||||
export {
|
||||
setBlockTracking,
|
||||
createTextVNode,
|
||||
createCommentVNode,
|
||||
createStaticVNode
|
||||
} from './vnode'
|
||||
// Since @vue/shared is inlined into final builds,
|
||||
// when re-exporting from @vue/shared we need to avoid relying on their original
|
||||
// types so that the bundled d.ts does not attempt to import from it.
|
||||
|
||||
@@ -8,7 +8,8 @@ import {
|
||||
VNode,
|
||||
VNodeArrayChildren,
|
||||
createVNode,
|
||||
isSameVNodeType
|
||||
isSameVNodeType,
|
||||
Static
|
||||
} from './vnode'
|
||||
import {
|
||||
ComponentInternalInstance,
|
||||
@@ -28,7 +29,8 @@ import {
|
||||
EMPTY_ARR,
|
||||
isReservedProp,
|
||||
isFunction,
|
||||
PatchFlags
|
||||
PatchFlags,
|
||||
NOOP
|
||||
} from '@vue/shared'
|
||||
import {
|
||||
queueJob,
|
||||
@@ -88,8 +90,15 @@ export interface RendererOptions<HostNode = any, HostElement = any> {
|
||||
setElementText(node: HostElement, text: string): void
|
||||
parentNode(node: HostNode): HostElement | null
|
||||
nextSibling(node: HostNode): HostNode | null
|
||||
querySelector(selector: string): HostElement | null
|
||||
setScopeId(el: HostNode, id: string): void
|
||||
querySelector?(selector: string): HostElement | null
|
||||
setScopeId?(el: HostElement, id: string): void
|
||||
cloneNode?(node: HostNode): HostNode
|
||||
insertStaticContent?(
|
||||
content: string,
|
||||
parent: HostElement,
|
||||
anchor: HostNode | null,
|
||||
isSVG: boolean
|
||||
): HostElement
|
||||
}
|
||||
|
||||
export type RootRenderFunction<HostNode, HostElement> = (
|
||||
@@ -197,7 +206,9 @@ export function createRenderer<
|
||||
parentNode: hostParentNode,
|
||||
nextSibling: hostNextSibling,
|
||||
querySelector: hostQuerySelector,
|
||||
setScopeId: hostSetScopeId
|
||||
setScopeId: hostSetScopeId = NOOP,
|
||||
cloneNode: hostCloneNode,
|
||||
insertStaticContent: hostInsertStaticContent
|
||||
} = options
|
||||
|
||||
const internals: RendererInternals<HostNode, HostElement> = {
|
||||
@@ -233,6 +244,11 @@ export function createRenderer<
|
||||
case Comment:
|
||||
processCommentNode(n1, n2, container, anchor)
|
||||
break
|
||||
case Static:
|
||||
if (n1 == null) {
|
||||
mountStaticNode(n2, container, anchor, isSVG)
|
||||
} // static nodes are noop on patch
|
||||
break
|
||||
case Fragment:
|
||||
processFragment(
|
||||
n1,
|
||||
@@ -336,6 +352,26 @@ export function createRenderer<
|
||||
}
|
||||
}
|
||||
|
||||
function mountStaticNode(
|
||||
n2: HostVNode,
|
||||
container: HostElement,
|
||||
anchor: HostNode | null,
|
||||
isSVG: boolean
|
||||
) {
|
||||
if (n2.el != null && hostCloneNode !== undefined) {
|
||||
hostInsert(hostCloneNode(n2.el), container, anchor)
|
||||
} else {
|
||||
// static nodes are only present when used with compiler-dom/runtime-dom
|
||||
// which guarantees presence of hostInsertStaticContent.
|
||||
n2.el = hostInsertStaticContent!(
|
||||
n2.children as string,
|
||||
container,
|
||||
anchor,
|
||||
isSVG
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function processElement(
|
||||
n1: HostVNode | null,
|
||||
n2: HostVNode,
|
||||
@@ -374,50 +410,58 @@ export function createRenderer<
|
||||
isSVG: boolean,
|
||||
optimized: boolean
|
||||
) {
|
||||
const el = (vnode.el = hostCreateElement(vnode.type as string, isSVG))
|
||||
let el: HostElement
|
||||
const { type, props, shapeFlag, transition, scopeId } = vnode
|
||||
|
||||
// props
|
||||
if (props != null) {
|
||||
for (const key in props) {
|
||||
if (isReservedProp(key)) continue
|
||||
hostPatchProp(el, key, props[key], null, isSVG)
|
||||
if (vnode.el != null && hostCloneNode !== undefined) {
|
||||
// If a vnode has non-null el, it means it's being reused.
|
||||
// Only static vnodes can be reused, so its mounted DOM nodes should be
|
||||
// exactly the same, and we can simply do a clone here.
|
||||
el = vnode.el = hostCloneNode(vnode.el) as HostElement
|
||||
} else {
|
||||
el = vnode.el = hostCreateElement(vnode.type as string, isSVG)
|
||||
// props
|
||||
if (props != null) {
|
||||
for (const key in props) {
|
||||
if (isReservedProp(key)) continue
|
||||
hostPatchProp(el, key, props[key], null, isSVG)
|
||||
}
|
||||
if (props.onVnodeBeforeMount != null) {
|
||||
invokeDirectiveHook(props.onVnodeBeforeMount, parentComponent, vnode)
|
||||
}
|
||||
}
|
||||
if (props.onVnodeBeforeMount != null) {
|
||||
invokeDirectiveHook(props.onVnodeBeforeMount, parentComponent, vnode)
|
||||
|
||||
// scopeId
|
||||
if (__BUNDLER__) {
|
||||
if (scopeId !== null) {
|
||||
hostSetScopeId(el, scopeId)
|
||||
}
|
||||
const treeOwnerId = parentComponent && parentComponent.type.__scopeId
|
||||
// vnode's own scopeId and the current patched component's scopeId is
|
||||
// different - this is a slot content node.
|
||||
if (treeOwnerId != null && treeOwnerId !== scopeId) {
|
||||
hostSetScopeId(el, treeOwnerId + '-s')
|
||||
}
|
||||
}
|
||||
|
||||
// children
|
||||
if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
|
||||
hostSetElementText(el, vnode.children as string)
|
||||
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
|
||||
mountChildren(
|
||||
vnode.children as HostVNodeChildren,
|
||||
el,
|
||||
null,
|
||||
parentComponent,
|
||||
parentSuspense,
|
||||
isSVG && type !== 'foreignObject',
|
||||
optimized || vnode.dynamicChildren !== null
|
||||
)
|
||||
}
|
||||
if (transition != null && !transition.persisted) {
|
||||
transition.beforeEnter(el)
|
||||
}
|
||||
}
|
||||
|
||||
// scopeId
|
||||
if (__BUNDLER__) {
|
||||
if (scopeId !== null) {
|
||||
hostSetScopeId(el, scopeId)
|
||||
}
|
||||
const treeOwnerId = parentComponent && parentComponent.type.__scopeId
|
||||
// vnode's own scopeId and the current patched component's scopeId is
|
||||
// different - this is a slot content node.
|
||||
if (treeOwnerId != null && treeOwnerId !== scopeId) {
|
||||
hostSetScopeId(el, treeOwnerId + '-s')
|
||||
}
|
||||
}
|
||||
|
||||
// children
|
||||
if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
|
||||
hostSetElementText(el, vnode.children as string)
|
||||
} else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
|
||||
mountChildren(
|
||||
vnode.children as HostVNodeChildren,
|
||||
el,
|
||||
null,
|
||||
parentComponent,
|
||||
parentSuspense,
|
||||
isSVG && type !== 'foreignObject',
|
||||
optimized || vnode.dynamicChildren !== null
|
||||
)
|
||||
}
|
||||
if (transition != null && !transition.persisted) {
|
||||
transition.beforeEnter(el)
|
||||
}
|
||||
hostInsert(el, container, anchor)
|
||||
const vnodeMountedHook = props && props.onVnodeMounted
|
||||
if (
|
||||
@@ -776,8 +820,14 @@ export function createRenderer<
|
||||
const targetSelector = n2.props && n2.props.target
|
||||
const { patchFlag, shapeFlag, children } = n2
|
||||
if (n1 == null) {
|
||||
if (__DEV__ && isString(targetSelector) && !hostQuerySelector) {
|
||||
warn(
|
||||
`Current renderer does not support string target for Portals. ` +
|
||||
`(missing querySelector renderer option)`
|
||||
)
|
||||
}
|
||||
const target = (n2.target = isString(targetSelector)
|
||||
? hostQuerySelector(targetSelector)
|
||||
? hostQuerySelector!(targetSelector)
|
||||
: targetSelector)
|
||||
if (target != null) {
|
||||
if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
|
||||
@@ -825,7 +875,7 @@ export function createRenderer<
|
||||
// target changed
|
||||
if (targetSelector !== (n1.props && n1.props.target)) {
|
||||
const nextTarget = (n2.target = isString(targetSelector)
|
||||
? hostQuerySelector(targetSelector)
|
||||
? hostQuerySelector!(targetSelector)
|
||||
: targetSelector)
|
||||
if (nextTarget != null) {
|
||||
// move content
|
||||
|
||||
@@ -39,6 +39,7 @@ export const Portal = (Symbol(__DEV__ ? 'Portal' : undefined) as any) as {
|
||||
}
|
||||
export const Text = Symbol(__DEV__ ? 'Text' : undefined)
|
||||
export const Comment = Symbol(__DEV__ ? 'Comment' : undefined)
|
||||
export const Static = Symbol(__DEV__ ? 'Static' : undefined)
|
||||
|
||||
export type VNodeTypes =
|
||||
| string
|
||||
@@ -46,6 +47,7 @@ export type VNodeTypes =
|
||||
| typeof Fragment
|
||||
| typeof Portal
|
||||
| typeof Text
|
||||
| typeof Static
|
||||
| typeof Comment
|
||||
| typeof SuspenseImpl
|
||||
|
||||
@@ -328,6 +330,10 @@ export function createTextVNode(text: string = ' ', flag: number = 0): VNode {
|
||||
return createVNode(Text, null, text, flag)
|
||||
}
|
||||
|
||||
export function createStaticVNode(content: string): VNode {
|
||||
return createVNode(Static, null, content)
|
||||
}
|
||||
|
||||
export function createCommentVNode(
|
||||
text: string = '',
|
||||
// when used as the v-else branch, the comment node must be created as a
|
||||
|
||||
Reference in New Issue
Block a user