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
nextSibling: hostNextSibling,
setScopeId: hostSetScopeId = NOOP,
cloneNode: hostCloneNode,
- insertStaticContent: hostInsertStaticContent,
- setStaticContent: hostSetStaticContent
+ insertStaticContent: hostInsertStaticContent
} = options
// Note: functions inside this closure should use `const xxx = () => {}`
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:
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,
+ isSVG
+ )
+ }
+ }
+
+ /**
+ * 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,
n2,
container,
- parentAnchor,
+ null,
parentComponent,
parentSuspense,
isSVG,
n1,
n2,
container,
- parentAnchor,
+ null,
parentComponent,
parentSuspense,
isSVG,
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 &&
return
}
+ if (__DEV__ && type === Static) {
+ removeStaticNode(vnode)
+ return
+ }
+
const performRemove = () => {
hostRemove(el!)
if (transition && !transition.persisted && transition.afterLeave) {
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
anchor: null,
target: null,
targetAnchor: null,
+ staticCount: 0,
shapeFlag,
patchFlag,
dynamicProps,
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.
/**
* @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
}
/**
(tempSVGContainer = doc.createElementNS(svgNS, 'svg'))
: tempContainer || (tempContainer = doc.createElement('div'))
temp.innerHTML = content
- const node = temp.children[0]
- 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
+ const first = temp.firstChild as Element
+ let node: Element | null = first
+ let last: Element = node
+ while (node) {
+ last = node
+ nodeOps.insert(node, parent, anchor)
+ node = temp.firstChild as Element
+ }
+ return [first, last]
}
}