Skip to content

Commit c0db807

Browse files
committed
refactor: simplify static content insertion
1 parent 5df7dfc commit c0db807

File tree

4 files changed

+30
-94
lines changed

4 files changed

+30
-94
lines changed

packages/runtime-core/src/renderer.ts

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,8 @@ export interface RendererOptions<
142142
content: string,
143143
parent: HostElement,
144144
anchor: HostNode | null,
145-
isSVG: boolean,
146-
cached?: HostNode[] | null
147-
): HostElement[]
145+
isSVG: boolean
146+
): [HostNode, HostNode]
148147
}
149148

150149
// Renderer Node can technically be any object in the context of core renderer
@@ -633,22 +632,12 @@ function baseCreateRenderer(
633632
) => {
634633
// static nodes are only present when used with compiler-dom/runtime-dom
635634
// which guarantees presence of hostInsertStaticContent.
636-
const nodes = hostInsertStaticContent!(
635+
;[n2.el, n2.anchor] = hostInsertStaticContent!(
637636
n2.children as string,
638637
container,
639638
anchor,
640-
isSVG,
641-
// pass cached nodes if the static node is being mounted multiple times
642-
// so that runtime-dom can simply cloneNode() instead of inserting new
643-
// HTML
644-
n2.staticCache
639+
isSVG
645640
)
646-
// first mount - this is the orignal hoisted vnode. cache nodes.
647-
if (!n2.el) {
648-
n2.staticCache = nodes
649-
}
650-
n2.el = nodes[0]
651-
n2.anchor = nodes[nodes.length - 1]
652641
}
653642

654643
/**

packages/runtime-core/src/vnode.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,6 @@ export interface VNode<
168168
target: HostElement | null // teleport target
169169
targetAnchor: HostNode | null // teleport target anchor
170170
staticCount?: number // number of elements contained in a static vnode
171-
staticCache?: HostNode[] // cache of parsed static nodes for faster repeated insertions
172171

173172
// suspense
174173
suspense: SuspenseBoundary | null
@@ -521,7 +520,6 @@ export function cloneVNode<T, U>(
521520
target: vnode.target,
522521
targetAnchor: vnode.targetAnchor,
523522
staticCount: vnode.staticCount,
524-
staticCache: vnode.staticCache,
525523
shapeFlag: vnode.shapeFlag,
526524
// if the vnode is cloned with extra props, we can no longer assume its
527525
// existing patch flag to be reliable and need to add the FULL_PROPS flag.

packages/runtime-dom/__tests__/nodeOps.spec.ts

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,8 @@ describe('runtime-dom: node-ops', () => {
3232
const parent = document.createElement('div')
3333
const nodes = nodeOps.insertStaticContent!(content, parent, null, false)
3434
expect(parent.innerHTML).toBe(content)
35-
expect(nodes.length).toBe(3)
3635
expect(nodes[0]).toBe(parent.firstChild)
37-
expect(nodes[nodes.length - 1]).toBe(parent.lastChild)
36+
expect(nodes[1]).toBe(parent.lastChild)
3837
})
3938

4039
test('fresh insertion with anchor', () => {
@@ -45,11 +44,8 @@ describe('runtime-dom: node-ops', () => {
4544
const anchor = parent.firstChild
4645
const nodes = nodeOps.insertStaticContent!(content, parent, anchor, false)
4746
expect(parent.innerHTML).toBe(content + existing)
48-
expect(nodes.length).toBe(3)
4947
expect(nodes[0]).toBe(parent.firstChild)
50-
expect(nodes[nodes.length - 1]).toBe(
51-
parent.childNodes[parent.childNodes.length - 2]
52-
)
48+
expect(nodes[1]).toBe(parent.childNodes[parent.childNodes.length - 2])
5349
})
5450

5551
test('fresh insertion as svg', () => {
@@ -86,32 +82,5 @@ describe('runtime-dom: node-ops', () => {
8682
expect(first.namespaceURI).toMatch('svg')
8783
expect(last.namespaceURI).toMatch('svg')
8884
})
89-
90-
test('cached', () => {
91-
const content = `<div>one</div><div>two</div>three`
92-
93-
const cacheParent = document.createElement('div')
94-
const nodes = nodeOps.insertStaticContent!(
95-
content,
96-
cacheParent,
97-
null,
98-
false
99-
)
100-
101-
const parent = document.createElement('div')
102-
103-
const clonedNodes = nodeOps.insertStaticContent!(
104-
``,
105-
parent,
106-
null,
107-
false,
108-
nodes
109-
)
110-
111-
expect(parent.innerHTML).toBe(content)
112-
expect(clonedNodes[0]).toBe(parent.firstChild)
113-
expect(clonedNodes[clonedNodes.length - 1]).toBe(parent.lastChild)
114-
expect(clonedNodes[0]).not.toBe(nodes[0])
115-
})
11685
})
11786
})

packages/runtime-dom/src/nodeOps.ts

Lines changed: 24 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ export const svgNS = 'http://www.w3.org/2000/svg'
44

55
const doc = (typeof document !== 'undefined' ? document : null) as Document
66

7+
const staticTemplateCache = new Map<string, DocumentFragment>()
8+
79
export const nodeOps: Omit<RendererOptions<Node, Element>, 'patchProp'> = {
810
insert: (child, parent, anchor) => {
911
parent.insertBefore(child, anchor || null)
@@ -68,55 +70,33 @@ export const nodeOps: Omit<RendererOptions<Node, Element>, 'patchProp'> = {
6870
},
6971

7072
// __UNSAFE__
71-
// Reason: insertAdjacentHTML.
73+
// Reason: innerHTML.
7274
// Static content here can only come from compiled templates.
7375
// As long as the user only uses trusted templates, this is safe.
74-
insertStaticContent(content, parent, anchor, isSVG, cached) {
75-
if (cached) {
76-
let first
77-
let last
78-
let i = 0
79-
let l = cached.length
80-
for (; i < l; i++) {
81-
const node = cached[i].cloneNode(true)
82-
if (i === 0) first = node
83-
if (i === l - 1) last = node
84-
parent.insertBefore(node, anchor)
85-
}
86-
return [first, last] as any
87-
}
88-
76+
insertStaticContent(content, parent, anchor, isSVG) {
8977
// <parent> before | first ... last | anchor </parent>
9078
const before = anchor ? anchor.previousSibling : parent.lastChild
91-
if (anchor) {
92-
let insertionPoint
93-
let usingTempInsertionPoint = false
94-
if (anchor instanceof Element) {
95-
insertionPoint = anchor
96-
} else {
97-
// insertAdjacentHTML only works for elements but the anchor is not an
98-
// element...
99-
usingTempInsertionPoint = true
100-
insertionPoint = isSVG
101-
? doc.createElementNS(svgNS, 'g')
102-
: doc.createElement('div')
103-
parent.insertBefore(insertionPoint, anchor)
104-
}
105-
insertionPoint.insertAdjacentHTML('beforebegin', content)
106-
if (usingTempInsertionPoint) {
107-
parent.removeChild(insertionPoint)
79+
let template = staticTemplateCache.get(content)
80+
if (!template) {
81+
const t = doc.createElement('template')
82+
t.innerHTML = isSVG ? `<svg>${content}</svg>` : content
83+
template = t.content
84+
if (isSVG) {
85+
// remove outer svg wrapper
86+
const wrapper = template.firstChild!
87+
while (wrapper.firstChild) {
88+
template.appendChild(wrapper.firstChild)
89+
}
90+
template.removeChild(wrapper)
10891
}
109-
} else {
110-
parent.insertAdjacentHTML('beforeend', content)
111-
}
112-
let first = before ? before.nextSibling : parent.firstChild
113-
const last = anchor ? anchor.previousSibling : parent.lastChild
114-
const ret = []
115-
while (first) {
116-
ret.push(first)
117-
if (first === last) break
118-
first = first.nextSibling
92+
staticTemplateCache.set(content, template)
11993
}
120-
return ret
94+
parent.insertBefore(template.cloneNode(true), anchor)
95+
return [
96+
// first
97+
before ? before.nextSibling! : parent.firstChild!,
98+
// last
99+
anchor ? anchor.previousSibling! : parent.lastChild!
100+
]
121101
}
122102
}

0 commit comments

Comments
 (0)