]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
refactor: simplify static content insertion
authorEvan You <yyx990803@gmail.com>
Fri, 9 Jul 2021 20:18:36 +0000 (16:18 -0400)
committerEvan You <yyx990803@gmail.com>
Thu, 15 Jul 2021 17:16:19 +0000 (13:16 -0400)
packages/runtime-core/src/renderer.ts
packages/runtime-core/src/vnode.ts
packages/runtime-dom/__tests__/nodeOps.spec.ts
packages/runtime-dom/src/nodeOps.ts

index dd2661ead7193e767878f7312af7fd0b7b71dbce..67c81aa4e16d2ab37c187ab3db2e9c6f24c3a6c8 100644 (file)
@@ -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]
   }
 
   /**
index 366340d99763249740539a5e7e1d1dce0e408369..e20632a7d757b731638ada071cdaebc0092899a4 100644 (file)
@@ -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<T, U>(
     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.
index aef6564ad8bd61b57457a5e6ca0dbccf82802af7..0e46ebfc9b4fd913e1219c759a86bb95bb121c6b 100644 (file)
@@ -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 = `<div>one</div><div>two</div>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])
-    })
   })
 })
index ceccf13b8b6522746f0e71de14c01d3e9ee6f8d7..de13d8f19d0c87df831e33b166bb27d2779cb61a 100644 (file)
@@ -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<string, DocumentFragment>()
+
 export const nodeOps: Omit<RendererOptions<Node, Element>, 'patchProp'> = {
   insert: (child, parent, anchor) => {
     parent.insertBefore(child, anchor || null)
@@ -68,55 +70,33 @@ export const nodeOps: Omit<RendererOptions<Node, Element>, '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) {
     // <parent> before | first ... last | anchor </parent>
     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)
-      }
-      insertionPoint.insertAdjacentHTML('beforebegin', content)
-      if (usingTempInsertionPoint) {
-        parent.removeChild(insertionPoint)
+    let template = staticTemplateCache.get(content)
+    if (!template) {
+      const t = doc.createElement('template')
+      t.innerHTML = isSVG ? `<svg>${content}</svg>` : content
+      template = t.content
+      if (isSVG) {
+        // remove outer svg wrapper
+        const wrapper = template.firstChild!
+        while (wrapper.firstChild) {
+          template.appendChild(wrapper.firstChild)
+        }
+        template.removeChild(wrapper)
       }
-    } else {
-      parent.insertAdjacentHTML('beforeend', content)
-    }
-    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
+      staticTemplateCache.set(content, template)
     }
-    return ret
+    parent.insertBefore(template.cloneNode(true), anchor)
+    return [
+      // first
+      before ? before.nextSibling! : parent.firstChild!,
+      // last
+      anchor ? anchor.previousSibling! : parent.lastChild!
+    ]
   }
 }