From: 三咲智子 Kevin Deng Date: Sun, 26 Nov 2023 22:22:10 +0000 (+0800) Subject: feat: append & prepend multiple elements X-Git-Tag: v3.6.0-alpha.1~16^2~805 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=71cf732d6d3f169d1b54e6e3c82ad1d67f1da961;p=thirdparty%2Fvuejs%2Fcore.git feat: append & prepend multiple elements --- diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.test.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.test.ts.snap index 020a7f31e8..da4d0ad4c3 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/compile.test.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.test.ts.snap @@ -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('
'); 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('369'); 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; } diff --git a/packages/compiler-vapor/__tests__/__snapshots__/fixtures.test.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/fixtures.test.ts.snap index 323534710b..f90d49cf64 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/fixtures.test.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/fixtures.test.ts.snap @@ -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(\\"

Counter

Count:

Double:

once:

{{ count }}

\\") 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) }) diff --git a/packages/compiler-vapor/__tests__/compile.test.ts b/packages/compiler-vapor/__tests__/compile.test.ts index 35aa87dab0..979bb094f2 100644 --- a/packages/compiler-vapor/__tests__/compile.test.ts +++ b/packages/compiler-vapor/__tests__/compile.test.ts @@ -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() }) diff --git a/packages/compiler-vapor/src/generate.ts b/packages/compiler-vapor/src/generate.ts index d101fcf696..f87f5fab51 100644 --- a/packages/compiler-vapor/src/generate.ts +++ b/packages/compiler-vapor/src/generate.ts @@ -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}: [` diff --git a/packages/compiler-vapor/src/ir.ts b/packages/compiler-vapor/src/ir.ts index bd4909fe58..136f11fc06 100644 --- a/packages/compiler-vapor/src/ir.ts +++ b/packages/compiler-vapor/src/ir.ts @@ -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 { diff --git a/packages/compiler-vapor/src/transform.ts b/packages/compiler-vapor/src/transform.ts index a5dee2a356..3c95269509 100644 --- a/packages/compiler-vapor/src/transform.ts +++ b/packages/compiler-vapor/src/transform.ts @@ -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, }) } } diff --git a/packages/runtime-vapor/src/render.ts b/packages/runtime-vapor/src/render.ts index 6c87b629bb..e916633546 100644 --- a/packages/runtime-vapor/src/render.ts +++ b/packages/runtime-vapor/src/render.ts @@ -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 index 0000000000..8a3787e7be --- /dev/null +++ b/playground/src/dynamic-mixed2.vue @@ -0,0 +1,3 @@ +