]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat: append & prepend multiple elements
author三咲智子 Kevin Deng <sxzz@sxzz.moe>
Sun, 26 Nov 2023 22:22:10 +0000 (06:22 +0800)
committer三咲智子 Kevin Deng <sxzz@sxzz.moe>
Sun, 26 Nov 2023 22:22:10 +0000 (06:22 +0800)
packages/compiler-vapor/__tests__/__snapshots__/compile.test.ts.snap
packages/compiler-vapor/__tests__/__snapshots__/fixtures.test.ts.snap
packages/compiler-vapor/__tests__/compile.test.ts
packages/compiler-vapor/src/generate.ts
packages/compiler-vapor/src/ir.ts
packages/compiler-vapor/src/transform.ts
packages/runtime-vapor/src/render.ts
playground/src/dynamic-mixed2.vue [new file with mode: 0644]

index 020a7f31e86c60d69af837c996be599cf0d0d240..da4d0ad4c3da3c407ba34dff470a5f82f478e2a8 100644 (file)
@@ -110,7 +110,7 @@ export function render() {
 `;
 
 exports[`comile > directives > v-once > basic 1`] = `
-"import { template, children, createTextNode, setText, setAttr, insert } from 'vue/vapor';
+"import { template, children, createTextNode, setText, setAttr, prepend } from 'vue/vapor';
 const t0 = template('<div> <span></span></div>');
 export function render() {
   const n0 = t0();
@@ -118,14 +118,14 @@ export function render() {
     0: [
       n3,
       {
-        2: [n2],
+        1: [n2],
       },
     ],
   } = children(n0);
   const n1 = createTextNode(msg.value);
   setText(n1, undefined, msg.value);
   setAttr(n2, 'class', undefined, clz.value);
-  insert(n1, n3, 0 /* InsertPosition.FIRST */);
+  prepend(n3, n1);
   return n0;
 }
 "
@@ -197,19 +197,49 @@ export function render() {
 
 exports[`comile > static + dynamic root 1`] = `
 "import { watchEffect } from 'vue';
-import { template, createTextNode, insert, setText } from 'vue/vapor';
-const t0 = template('2');
+import { template, children, createTextNode, prepend, insert, append, setText } from 'vue/vapor';
+const t0 = template('3<!>6<!>9');
 export function render() {
   const n0 = t0();
+  const {
+    1: [n9],
+    3: [n10],
+  } = children(n0);
   const n1 = createTextNode(1);
-  const n2 = createTextNode(3);
-  insert(n1, n0, 0 /* InsertPosition.FIRST */);
-  insert(n2, n0);
+  const n2 = createTextNode(2);
+  const n3 = createTextNode(4);
+  const n4 = createTextNode(5);
+  const n5 = createTextNode(7);
+  const n6 = createTextNode(8);
+  const n7 = createTextNode('A');
+  const n8 = createTextNode('B');
+  prepend(n0, n1, n2);
+  insert([n3, n4], n0, n9);
+  insert([n5, n6], n0, n10);
+  append(n0, n7, n8);
   watchEffect(() => {
     setText(n1, undefined, 1);
   });
   watchEffect(() => {
-    setText(n2, undefined, 3);
+    setText(n2, undefined, 2);
+  });
+  watchEffect(() => {
+    setText(n3, undefined, 4);
+  });
+  watchEffect(() => {
+    setText(n4, undefined, 5);
+  });
+  watchEffect(() => {
+    setText(n5, undefined, 7);
+  });
+  watchEffect(() => {
+    setText(n6, undefined, 8);
+  });
+  watchEffect(() => {
+    setText(n7, undefined, 'A');
+  });
+  watchEffect(() => {
+    setText(n8, undefined, 'B');
   });
   return n0;
 }
index 323534710bb99c8b3b8a5a4ccfb97fd3fd1f5fa7..f90d49cf64952267dea9ad09718667253699b673 100644 (file)
@@ -3,7 +3,7 @@
 exports[`fixtures 1`] = `
 "import { defineComponent as _defineComponent } from 'vue'
 import { watchEffect } from 'vue'
-import { template, children, createTextNode, insert, setText, on, setHtml } from 'vue/vapor'
+import { template, children, createTextNode, append, setText, on, setHtml } from 'vue/vapor'
 const t0 = template(\\"<h1 id=\\\\\\"title\\\\\\">Counter</h1><p>Count: </p><p>Double: </p><button>Increment</button><div></div><input type=\\\\\\"text\\\\\\"><p>once: </p><p>{{ count }}</p>\\")
 import { ref, computed } from 'vue'
 
@@ -22,12 +22,12 @@ return (() => {
 const n0 = t0()
 const { 1: [n2], 2: [n4], 3: [n5], 4: [n6], 6: [n8],} = children(n0)
 const n1 = createTextNode(count.value)
-insert(n1, n2)
+append(n2, n1)
 const n3 = createTextNode(double.value)
-insert(n3, n4)
+append(n4, n3)
 const n7 = createTextNode(count.value)
 setText(n7, undefined, count.value)
-insert(n7, n8)
+append(n8, n7)
 watchEffect(() => {
 setText(n1, undefined, count.value)
 })
index 35aa87dab019157e0ef1e16d5f5ac29d596683f3..979bb094f2c7ffc8ec7044a6d2e911715f8b0cfd 100644 (file)
@@ -34,7 +34,9 @@ describe('comile', () => {
   })
 
   test('static + dynamic root', async () => {
-    const code = await compile(`{{ 1 }}2{{ 3 }}`)
+    const code = await compile(
+      `{{ 1 }}{{ 2 }}3{{ 4 }}{{ 5 }}6{{ 7 }}{{ 8 }}9{{ 'A' }}{{ 'B' }}`,
+    )
     expect(code).matchSnapshot()
   })
 
index d101fcf696fe35db1ae59da56ce44e82fe0344ed..f87f5fab51ddb8c1408dab7fce4891016b6f490a 100644 (file)
@@ -119,16 +119,20 @@ export function generate(
       }
 
       case IRNodeTypes.INSERT_NODE: {
-        let anchor = ''
-        if (typeof oper.anchor === 'number') {
-          anchor = `, n${oper.anchor}`
-        } else if (oper.anchor === 'first') {
-          anchor = `, 0 /* InsertPosition.FIRST */`
-        }
-        code = `insert(n${oper.element}, n${oper.parent}${anchor})\n`
+        const elements = ([] as number[]).concat(oper.element)
+        let element = elements.map((el) => `n${el}`).join(', ')
+        if (elements.length > 1) element = `[${element}]`
+        code = `insert(${element}, n${oper.parent}${`, n${oper.anchor}`})\n`
         vaporHelpers.add('insert')
         break
       }
+      case IRNodeTypes.PREPEND_NODE: {
+        code = `prepend(n${oper.parent}, ${oper.elements
+          .map((el) => `n${el}`)
+          .join(', ')})\n`
+        vaporHelpers.add('prepend')
+        break
+      }
       case IRNodeTypes.APPEND_NODE: {
         code = `append(n${oper.parent}, ${oper.elements
           .map((el) => `n${el}`)
@@ -148,11 +152,12 @@ function genChildren(children: DynamicChildren) {
   let code = ''
   // TODO
   let offset = 0
-
   for (const [index, child] of Object.entries(children)) {
     const childrenLength = Object.keys(child.children).length
-    if (child.ghost && child.placeholder === null && childrenLength === 0)
+    if (child.ghost && child.placeholder === null && childrenLength === 0) {
+      offset--
       continue
+    }
 
     code += ` ${Number(index) + offset}: [`
 
index bd4909fe588e1f1213138cb414b43663ccdd46cc..136f11fc06cac6f268f0f4028d584648ee0a4070 100644 (file)
@@ -11,6 +11,7 @@ export const enum IRNodeTypes {
   SET_HTML,
 
   INSERT_NODE,
+  PREPEND_NODE,
   APPEND_NODE,
   CREATE_TEXT_NODE,
 }
@@ -72,12 +73,17 @@ export interface CreateTextNodeIRNode extends IRNode {
   value: string
 }
 
-export type InsertAnchor = number | 'first' | 'last'
 export interface InsertNodeIRNode extends IRNode {
   type: IRNodeTypes.INSERT_NODE
-  element: number
+  element: number | number[]
+  parent: number
+  anchor: number
+}
+
+export interface PrependNodeIRNode extends IRNode {
+  type: IRNodeTypes.PREPEND_NODE
+  elements: number[]
   parent: number
-  anchor: InsertAnchor
 }
 
 export interface AppendNodeIRNode extends IRNode {
@@ -93,6 +99,7 @@ export type OperationNode =
   | SetHtmlIRNode
   | CreateTextNodeIRNode
   | InsertNodeIRNode
+  | PrependNodeIRNode
   | AppendNodeIRNode
 
 export interface DynamicInfo {
index a5dee2a356034a59b7cd50f8f9ce73f26ef89fed..3c95269509c42c445674376a9c32918f3e899dcc 100644 (file)
@@ -15,7 +15,6 @@ import {
   type RootIRNode,
   IRNodeTypes,
   DynamicInfo,
-  InsertAnchor,
 } from './ir'
 import { isVoidTag } from '@vue/shared'
 
@@ -170,41 +169,46 @@ function transformChildren(
   const childrenTemplate: string[] = []
   children.forEach((child, i) => walkNode(child, i))
 
-  const dynamicChildren = Object.values(ctx.dynamic.children)
-  const dynamicCount = dynamicChildren.reduce(
-    (prev, child) => prev + (child.ghost ? 1 : 0),
-    0,
-  )
-  if (dynamicCount === children.length) {
-    // all dynamic node
-    ctx.registerOpration({
-      type: IRNodeTypes.APPEND_NODE,
-      loc: ctx.node.loc,
-      elements: dynamicChildren.map((child) => child.id!),
-      parent: ctx.reference(),
-    })
-  } else if (dynamicCount > 0 && dynamicCount < children.length) {
-    // mixed
-    for (const [indexString, child] of Object.entries(ctx.dynamic.children)) {
-      if (!child.ghost) continue
-
-      const index = Number(indexString)
-      let anchor: InsertAnchor
-      if (index === 0) {
-        anchor = 'first'
-      } else if (index === children.length - 1) {
-        anchor = 'last'
-      } else {
-        childrenTemplate[index] = `<!>`
-        anchor = child.placeholder = ctx.incraseId()
-      }
+  let prevChildren: DynamicInfo[] = []
+  let hasStatic = false
+
+  for (let index = 0; index < children.length; index++) {
+    const child = ctx.dynamic.children[index]
+
+    if (!child || !child.ghost) {
+      if (prevChildren.length)
+        if (hasStatic) {
+          childrenTemplate[index - prevChildren.length] = `<!>`
+          const anchor = (prevChildren[0].placeholder = ctx.incraseId())
+
+          ctx.registerOpration({
+            type: IRNodeTypes.INSERT_NODE,
+            loc: ctx.node.loc,
+            element: prevChildren.map((child) => child.id!),
+            parent: ctx.reference(),
+            anchor,
+          })
+        } else {
+          ctx.registerOpration({
+            type: IRNodeTypes.PREPEND_NODE,
+            loc: ctx.node.loc,
+            elements: prevChildren.map((child) => child.id!),
+            parent: ctx.reference(),
+          })
+        }
+      hasStatic = true
+      prevChildren = []
+      continue
+    }
+
+    prevChildren.push(child)
 
+    if (index === children.length - 1) {
       ctx.registerOpration({
-        type: IRNodeTypes.INSERT_NODE,
+        type: IRNodeTypes.APPEND_NODE,
         loc: ctx.node.loc,
-        element: child.id!,
+        elements: prevChildren.map((child) => child.id!),
         parent: ctx.reference(),
-        anchor,
       })
     }
   }
index 6c87b629bb98f1b33ccc9fe7a4b29a6225970f71..e9166335464b653c7d9ef0e19d0c5c207d3f71d6 100644 (file)
@@ -58,6 +58,14 @@ export function insert(
   // }
 }
 
+export function prepend(parent: ParentBlock, ...nodes: Node[]) {
+  if (parent instanceof Node) {
+    parent.prepend(...nodes)
+  } else if (isArray(parent)) {
+    parent.unshift(...nodes)
+  }
+}
+
 export function append(parent: ParentBlock, ...nodes: Node[]) {
   if (parent instanceof Node) {
     parent.append(...nodes)
diff --git a/playground/src/dynamic-mixed2.vue b/playground/src/dynamic-mixed2.vue
new file mode 100644 (file)
index 0000000..8a3787e
--- /dev/null
@@ -0,0 +1,3 @@
+<template>
+  {{ 1 }}{{ 2 }}3{{ 4 }}{{ 5 }}6{{ 7 }}{{ 8 }}9{{ 'A' }}{{ 'B' }}
+</template>