wip(runtime): support multi-element static vnode in renderer
This commit is contained in:
parent
cb9444807e
commit
dbf627f136
@ -116,8 +116,7 @@ export interface RendererOptions<
|
||||
parent: HostElement,
|
||||
anchor: HostNode | null,
|
||||
isSVG: boolean
|
||||
): HostElement
|
||||
setStaticContent?(node: HostElement, content: string): void
|
||||
): HostElement[]
|
||||
}
|
||||
|
||||
// Renderer Node can technically be any object in the context of core renderer
|
||||
@ -333,8 +332,7 @@ function baseCreateRenderer(
|
||||
nextSibling: hostNextSibling,
|
||||
setScopeId: hostSetScopeId = NOOP,
|
||||
cloneNode: hostCloneNode,
|
||||
insertStaticContent: hostInsertStaticContent,
|
||||
setStaticContent: hostSetStaticContent
|
||||
insertStaticContent: hostInsertStaticContent
|
||||
} = options
|
||||
|
||||
// Note: functions inside this closure should use `const xxx = () => {}`
|
||||
@ -373,11 +371,7 @@ function baseCreateRenderer(
|
||||
if (n1 == null) {
|
||||
mountStaticNode(n2, container, anchor, isSVG)
|
||||
} else if (__DEV__) {
|
||||
// static nodes are only patched during dev for HMR
|
||||
n2.el = n1.el
|
||||
if (n2.children !== n1.children) {
|
||||
hostSetStaticContent!(n2.el!, n2.children as string)
|
||||
}
|
||||
patchStaticNode(n1, n2, container, isSVG)
|
||||
}
|
||||
break
|
||||
case Fragment:
|
||||
@ -492,11 +486,19 @@ function baseCreateRenderer(
|
||||
isSVG: boolean
|
||||
) => {
|
||||
if (n2.el && hostCloneNode !== undefined) {
|
||||
hostInsert(hostCloneNode(n2.el), container, anchor)
|
||||
// static node was already mounted (and reused), or adopted
|
||||
// server-rendered node during hydration (in this case its children can be
|
||||
// stripped by SSR optimizations). Clone the dom nodes instead.
|
||||
let cur: RendererElement | null = n2.el
|
||||
while (cur && cur !== n2.anchor) {
|
||||
hostInsert(hostCloneNode(cur), container, anchor)
|
||||
cur = hostNextSibling(cur)
|
||||
}
|
||||
hostInsert(hostCloneNode(n2.anchor!), container, anchor)
|
||||
} else {
|
||||
// static nodes are only present when used with compiler-dom/runtime-dom
|
||||
// which guarantees presence of hostInsertStaticContent.
|
||||
n2.el = hostInsertStaticContent!(
|
||||
;[n2.el, n2.anchor] = hostInsertStaticContent!(
|
||||
n2.children as string,
|
||||
container,
|
||||
anchor,
|
||||
@ -505,6 +507,64 @@ function baseCreateRenderer(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dev / HMR only
|
||||
*/
|
||||
const patchStaticNode = (
|
||||
n1: VNode,
|
||||
n2: VNode,
|
||||
container: RendererElement,
|
||||
isSVG: boolean
|
||||
) => {
|
||||
// static nodes are only patched during dev for HMR
|
||||
if (n2.children !== n1.children) {
|
||||
const anchor = hostNextSibling(n1.anchor!)
|
||||
// remove existing
|
||||
removeStaticNode(n1)
|
||||
// insert new
|
||||
;[n2.el, n2.anchor] = hostInsertStaticContent!(
|
||||
n2.children as string,
|
||||
container,
|
||||
anchor,
|
||||
isSVG
|
||||
)
|
||||
} else {
|
||||
n2.el = n1.el
|
||||
n2.anchor = n1.anchor
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dev / HMR only
|
||||
*/
|
||||
const moveStaticNode = (
|
||||
vnode: VNode,
|
||||
container: RendererElement,
|
||||
anchor: RendererNode | null
|
||||
) => {
|
||||
let cur = vnode.el
|
||||
const end = vnode.anchor!
|
||||
while (cur && cur !== end) {
|
||||
const next = hostNextSibling(cur)
|
||||
hostInsert(cur, container, anchor)
|
||||
cur = next
|
||||
}
|
||||
hostInsert(end, container, anchor)
|
||||
}
|
||||
|
||||
/**
|
||||
* Dev / HMR only
|
||||
*/
|
||||
const removeStaticNode = (vnode: VNode) => {
|
||||
let cur = vnode.el
|
||||
while (cur && cur !== vnode.anchor) {
|
||||
const next = hostNextSibling(cur)
|
||||
hostRemove(cur)
|
||||
cur = next
|
||||
}
|
||||
hostRemove(vnode.anchor!)
|
||||
}
|
||||
|
||||
const processElement = (
|
||||
n1: VNode | null,
|
||||
n2: VNode,
|
||||
@ -1456,7 +1516,7 @@ function baseCreateRenderer(
|
||||
n1,
|
||||
n2,
|
||||
container,
|
||||
parentAnchor,
|
||||
null,
|
||||
parentComponent,
|
||||
parentSuspense,
|
||||
isSVG,
|
||||
@ -1481,7 +1541,7 @@ function baseCreateRenderer(
|
||||
n1,
|
||||
n2,
|
||||
container,
|
||||
parentAnchor,
|
||||
null,
|
||||
parentComponent,
|
||||
parentSuspense,
|
||||
isSVG,
|
||||
@ -1692,6 +1752,12 @@ function baseCreateRenderer(
|
||||
return
|
||||
}
|
||||
|
||||
// static node move can only happen when force updating HMR
|
||||
if (__DEV__ && type === Static) {
|
||||
moveStaticNode(vnode, container, anchor)
|
||||
return
|
||||
}
|
||||
|
||||
// single nodes
|
||||
const needTransition =
|
||||
moveType !== MoveType.REORDER &&
|
||||
@ -1808,6 +1874,11 @@ function baseCreateRenderer(
|
||||
return
|
||||
}
|
||||
|
||||
if (__DEV__ && type === Static) {
|
||||
removeStaticNode(vnode)
|
||||
return
|
||||
}
|
||||
|
||||
const performRemove = () => {
|
||||
hostRemove(el!)
|
||||
if (transition && !transition.persisted && transition.afterLeave) {
|
||||
|
@ -127,6 +127,7 @@ export interface VNode<HostNode = RendererNode, HostElement = RendererElement> {
|
||||
anchor: HostNode | null // fragment anchor
|
||||
target: HostElement | null // teleport target
|
||||
targetAnchor: HostNode | null // teleport target anchor
|
||||
staticCount: number // number of elements contained in a static vnode
|
||||
|
||||
// optimization only
|
||||
shapeFlag: number
|
||||
@ -368,6 +369,7 @@ function _createVNode(
|
||||
anchor: null,
|
||||
target: null,
|
||||
targetAnchor: null,
|
||||
staticCount: 0,
|
||||
shapeFlag,
|
||||
patchFlag,
|
||||
dynamicProps,
|
||||
@ -422,6 +424,7 @@ export function cloneVNode<T, U>(
|
||||
children: vnode.children,
|
||||
target: vnode.target,
|
||||
targetAnchor: vnode.targetAnchor,
|
||||
staticCount: vnode.staticCount,
|
||||
shapeFlag: vnode.shapeFlag,
|
||||
// if the vnode is cloned with extra props, we can no longer assume its
|
||||
// existing patch flag to be reliable and need to bail out of optimized mode.
|
||||
@ -459,8 +462,15 @@ export function createTextVNode(text: string = ' ', flag: number = 0): VNode {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function createStaticVNode(content: string): VNode {
|
||||
return createVNode(Static, null, content)
|
||||
export function createStaticVNode(
|
||||
content: string,
|
||||
numberOfNodes: number
|
||||
): VNode {
|
||||
// A static vnode can contain multiple stringified elements, and the number
|
||||
// of elements is necessary for hydration.
|
||||
const vnode = createVNode(Static, null, content)
|
||||
vnode.staticCount = numberOfNodes
|
||||
return vnode
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -64,17 +64,14 @@ export const nodeOps: Omit<RendererOptions<Node, Element>, 'patchProp'> = {
|
||||
(tempSVGContainer = doc.createElementNS(svgNS, 'svg'))
|
||||
: tempContainer || (tempContainer = doc.createElement('div'))
|
||||
temp.innerHTML = content
|
||||
const node = temp.children[0]
|
||||
const first = temp.firstChild as Element
|
||||
let node: Element | null = first
|
||||
let last: Element = node
|
||||
while (node) {
|
||||
last = node
|
||||
nodeOps.insert(node, parent, anchor)
|
||||
return node
|
||||
}
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
// __UNSAFE__
|
||||
// Reason: innerHTML.
|
||||
// same as `insertStaticContent`, but this is also dev only (for HMR).
|
||||
nodeOps.setStaticContent = (el, content) => {
|
||||
el.innerHTML = content
|
||||
node = temp.firstChild as Element
|
||||
}
|
||||
return [first, last]
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user