diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index dd2661ea..67c81aa4 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -142,9 +142,8 @@ export interface RendererOptions< content: string, parent: HostElement, anchor: HostNode | null, - isSVG: boolean, - cached?: HostNode[] | null - ): HostElement[] + isSVG: boolean + ): [HostNode, HostNode] } // Renderer Node can technically be any object in the context of core renderer @@ -633,22 +632,12 @@ function baseCreateRenderer( ) => { // static nodes are only present when used with compiler-dom/runtime-dom // which guarantees presence of hostInsertStaticContent. - const nodes = hostInsertStaticContent!( + ;[n2.el, n2.anchor] = hostInsertStaticContent!( n2.children as string, container, anchor, - isSVG, - // pass cached nodes if the static node is being mounted multiple times - // so that runtime-dom can simply cloneNode() instead of inserting new - // HTML - n2.staticCache + isSVG ) - // first mount - this is the orignal hoisted vnode. cache nodes. - if (!n2.el) { - n2.staticCache = nodes - } - n2.el = nodes[0] - n2.anchor = nodes[nodes.length - 1] } /** diff --git a/packages/runtime-core/src/vnode.ts b/packages/runtime-core/src/vnode.ts index 366340d9..e20632a7 100644 --- a/packages/runtime-core/src/vnode.ts +++ b/packages/runtime-core/src/vnode.ts @@ -168,7 +168,6 @@ export interface VNode< target: HostElement | null // teleport target targetAnchor: HostNode | null // teleport target anchor staticCount?: number // number of elements contained in a static vnode - staticCache?: HostNode[] // cache of parsed static nodes for faster repeated insertions // suspense suspense: SuspenseBoundary | null @@ -521,7 +520,6 @@ export function cloneVNode( target: vnode.target, targetAnchor: vnode.targetAnchor, staticCount: vnode.staticCount, - staticCache: vnode.staticCache, 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 add the FULL_PROPS flag. diff --git a/packages/runtime-dom/__tests__/nodeOps.spec.ts b/packages/runtime-dom/__tests__/nodeOps.spec.ts index aef6564a..0e46ebfc 100644 --- a/packages/runtime-dom/__tests__/nodeOps.spec.ts +++ b/packages/runtime-dom/__tests__/nodeOps.spec.ts @@ -32,9 +32,8 @@ describe('runtime-dom: node-ops', () => { const parent = document.createElement('div') const nodes = nodeOps.insertStaticContent!(content, parent, null, false) expect(parent.innerHTML).toBe(content) - expect(nodes.length).toBe(3) expect(nodes[0]).toBe(parent.firstChild) - expect(nodes[nodes.length - 1]).toBe(parent.lastChild) + expect(nodes[1]).toBe(parent.lastChild) }) test('fresh insertion with anchor', () => { @@ -45,11 +44,8 @@ describe('runtime-dom: node-ops', () => { const anchor = parent.firstChild const nodes = nodeOps.insertStaticContent!(content, parent, anchor, false) expect(parent.innerHTML).toBe(content + existing) - expect(nodes.length).toBe(3) expect(nodes[0]).toBe(parent.firstChild) - expect(nodes[nodes.length - 1]).toBe( - parent.childNodes[parent.childNodes.length - 2] - ) + expect(nodes[1]).toBe(parent.childNodes[parent.childNodes.length - 2]) }) test('fresh insertion as svg', () => { @@ -86,32 +82,5 @@ describe('runtime-dom: node-ops', () => { expect(first.namespaceURI).toMatch('svg') expect(last.namespaceURI).toMatch('svg') }) - - test('cached', () => { - const content = `
one
two
three` - - const cacheParent = document.createElement('div') - const nodes = nodeOps.insertStaticContent!( - content, - cacheParent, - null, - false - ) - - const parent = document.createElement('div') - - const clonedNodes = nodeOps.insertStaticContent!( - ``, - parent, - null, - false, - nodes - ) - - expect(parent.innerHTML).toBe(content) - expect(clonedNodes[0]).toBe(parent.firstChild) - expect(clonedNodes[clonedNodes.length - 1]).toBe(parent.lastChild) - expect(clonedNodes[0]).not.toBe(nodes[0]) - }) }) }) diff --git a/packages/runtime-dom/src/nodeOps.ts b/packages/runtime-dom/src/nodeOps.ts index ceccf13b..de13d8f1 100644 --- a/packages/runtime-dom/src/nodeOps.ts +++ b/packages/runtime-dom/src/nodeOps.ts @@ -4,6 +4,8 @@ export const svgNS = 'http://www.w3.org/2000/svg' const doc = (typeof document !== 'undefined' ? document : null) as Document +const staticTemplateCache = new Map() + export const nodeOps: Omit, 'patchProp'> = { insert: (child, parent, anchor) => { parent.insertBefore(child, anchor || null) @@ -68,55 +70,33 @@ export const nodeOps: Omit, 'patchProp'> = { }, // __UNSAFE__ - // Reason: insertAdjacentHTML. + // Reason: innerHTML. // Static content here can only come from compiled templates. // As long as the user only uses trusted templates, this is safe. - insertStaticContent(content, parent, anchor, isSVG, cached) { - if (cached) { - let first - let last - let i = 0 - let l = cached.length - for (; i < l; i++) { - const node = cached[i].cloneNode(true) - if (i === 0) first = node - if (i === l - 1) last = node - parent.insertBefore(node, anchor) - } - return [first, last] as any - } - + insertStaticContent(content, parent, anchor, isSVG) { // before | first ... last | anchor const before = anchor ? anchor.previousSibling : parent.lastChild - if (anchor) { - let insertionPoint - let usingTempInsertionPoint = false - if (anchor instanceof Element) { - insertionPoint = anchor - } else { - // insertAdjacentHTML only works for elements but the anchor is not an - // element... - usingTempInsertionPoint = true - insertionPoint = isSVG - ? doc.createElementNS(svgNS, 'g') - : doc.createElement('div') - parent.insertBefore(insertionPoint, anchor) + let template = staticTemplateCache.get(content) + if (!template) { + const t = doc.createElement('template') + t.innerHTML = isSVG ? `${content}` : content + template = t.content + if (isSVG) { + // remove outer svg wrapper + const wrapper = template.firstChild! + while (wrapper.firstChild) { + template.appendChild(wrapper.firstChild) + } + template.removeChild(wrapper) } - insertionPoint.insertAdjacentHTML('beforebegin', content) - if (usingTempInsertionPoint) { - parent.removeChild(insertionPoint) - } - } else { - parent.insertAdjacentHTML('beforeend', content) + staticTemplateCache.set(content, template) } - let first = before ? before.nextSibling : parent.firstChild - const last = anchor ? anchor.previousSibling : parent.lastChild - const ret = [] - while (first) { - ret.push(first) - if (first === last) break - first = first.nextSibling - } - return ret + parent.insertBefore(template.cloneNode(true), anchor) + return [ + // first + before ? before.nextSibling! : parent.firstChild!, + // last + anchor ? anchor.previousSibling! : parent.lastChild! + ] } }