feat: append & prepend multiple elements

This commit is contained in:
三咲智子 Kevin Deng 2023-11-27 06:22:10 +08:00
parent 6ff8b1bf0d
commit 71cf732d6d
No known key found for this signature in database
GPG Key ID: 69992F2250DFD93E
8 changed files with 116 additions and 57 deletions

View File

@ -110,7 +110,7 @@ export function render() {
`; `;
exports[`comile > directives > v-once > basic 1`] = ` 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>'); const t0 = template('<div> <span></span></div>');
export function render() { export function render() {
const n0 = t0(); const n0 = t0();
@ -118,14 +118,14 @@ export function render() {
0: [ 0: [
n3, n3,
{ {
2: [n2], 1: [n2],
}, },
], ],
} = children(n0); } = children(n0);
const n1 = createTextNode(msg.value); const n1 = createTextNode(msg.value);
setText(n1, undefined, msg.value); setText(n1, undefined, msg.value);
setAttr(n2, 'class', undefined, clz.value); setAttr(n2, 'class', undefined, clz.value);
insert(n1, n3, 0 /* InsertPosition.FIRST */); prepend(n3, n1);
return n0; return n0;
} }
" "
@ -197,19 +197,49 @@ export function render() {
exports[`comile > static + dynamic root 1`] = ` exports[`comile > static + dynamic root 1`] = `
"import { watchEffect } from 'vue'; "import { watchEffect } from 'vue';
import { template, createTextNode, insert, setText } from 'vue/vapor'; import { template, children, createTextNode, prepend, insert, append, setText } from 'vue/vapor';
const t0 = template('2'); const t0 = template('3<!>6<!>9');
export function render() { export function render() {
const n0 = t0(); const n0 = t0();
const {
1: [n9],
3: [n10],
} = children(n0);
const n1 = createTextNode(1); const n1 = createTextNode(1);
const n2 = createTextNode(3); const n2 = createTextNode(2);
insert(n1, n0, 0 /* InsertPosition.FIRST */); const n3 = createTextNode(4);
insert(n2, n0); 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(() => { watchEffect(() => {
setText(n1, undefined, 1); setText(n1, undefined, 1);
}); });
watchEffect(() => { 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; return n0;
} }

View File

@ -3,7 +3,7 @@
exports[`fixtures 1`] = ` exports[`fixtures 1`] = `
"import { defineComponent as _defineComponent } from 'vue' "import { defineComponent as _defineComponent } from 'vue'
import { watchEffect } 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>\\") 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' import { ref, computed } from 'vue'
@ -22,12 +22,12 @@ return (() => {
const n0 = t0() const n0 = t0()
const { 1: [n2], 2: [n4], 3: [n5], 4: [n6], 6: [n8],} = children(n0) const { 1: [n2], 2: [n4], 3: [n5], 4: [n6], 6: [n8],} = children(n0)
const n1 = createTextNode(count.value) const n1 = createTextNode(count.value)
insert(n1, n2) append(n2, n1)
const n3 = createTextNode(double.value) const n3 = createTextNode(double.value)
insert(n3, n4) append(n4, n3)
const n7 = createTextNode(count.value) const n7 = createTextNode(count.value)
setText(n7, undefined, count.value) setText(n7, undefined, count.value)
insert(n7, n8) append(n8, n7)
watchEffect(() => { watchEffect(() => {
setText(n1, undefined, count.value) setText(n1, undefined, count.value)
}) })

View File

@ -34,7 +34,9 @@ describe('comile', () => {
}) })
test('static + dynamic root', async () => { 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() expect(code).matchSnapshot()
}) })

View File

@ -119,16 +119,20 @@ export function generate(
} }
case IRNodeTypes.INSERT_NODE: { case IRNodeTypes.INSERT_NODE: {
let anchor = '' const elements = ([] as number[]).concat(oper.element)
if (typeof oper.anchor === 'number') { let element = elements.map((el) => `n${el}`).join(', ')
anchor = `, n${oper.anchor}` if (elements.length > 1) element = `[${element}]`
} else if (oper.anchor === 'first') { code = `insert(${element}, n${oper.parent}${`, n${oper.anchor}`})\n`
anchor = `, 0 /* InsertPosition.FIRST */`
}
code = `insert(n${oper.element}, n${oper.parent}${anchor})\n`
vaporHelpers.add('insert') vaporHelpers.add('insert')
break 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: { case IRNodeTypes.APPEND_NODE: {
code = `append(n${oper.parent}, ${oper.elements code = `append(n${oper.parent}, ${oper.elements
.map((el) => `n${el}`) .map((el) => `n${el}`)
@ -148,11 +152,12 @@ function genChildren(children: DynamicChildren) {
let code = '' let code = ''
// TODO // TODO
let offset = 0 let offset = 0
for (const [index, child] of Object.entries(children)) { for (const [index, child] of Object.entries(children)) {
const childrenLength = Object.keys(child.children).length 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 continue
}
code += ` ${Number(index) + offset}: [` code += ` ${Number(index) + offset}: [`

View File

@ -11,6 +11,7 @@ export const enum IRNodeTypes {
SET_HTML, SET_HTML,
INSERT_NODE, INSERT_NODE,
PREPEND_NODE,
APPEND_NODE, APPEND_NODE,
CREATE_TEXT_NODE, CREATE_TEXT_NODE,
} }
@ -72,12 +73,17 @@ export interface CreateTextNodeIRNode extends IRNode {
value: string value: string
} }
export type InsertAnchor = number | 'first' | 'last'
export interface InsertNodeIRNode extends IRNode { export interface InsertNodeIRNode extends IRNode {
type: IRNodeTypes.INSERT_NODE 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 parent: number
anchor: InsertAnchor
} }
export interface AppendNodeIRNode extends IRNode { export interface AppendNodeIRNode extends IRNode {
@ -93,6 +99,7 @@ export type OperationNode =
| SetHtmlIRNode | SetHtmlIRNode
| CreateTextNodeIRNode | CreateTextNodeIRNode
| InsertNodeIRNode | InsertNodeIRNode
| PrependNodeIRNode
| AppendNodeIRNode | AppendNodeIRNode
export interface DynamicInfo { export interface DynamicInfo {

View File

@ -15,7 +15,6 @@ import {
type RootIRNode, type RootIRNode,
IRNodeTypes, IRNodeTypes,
DynamicInfo, DynamicInfo,
InsertAnchor,
} from './ir' } from './ir'
import { isVoidTag } from '@vue/shared' import { isVoidTag } from '@vue/shared'
@ -170,41 +169,46 @@ function transformChildren(
const childrenTemplate: string[] = [] const childrenTemplate: string[] = []
children.forEach((child, i) => walkNode(child, i)) children.forEach((child, i) => walkNode(child, i))
const dynamicChildren = Object.values(ctx.dynamic.children) let prevChildren: DynamicInfo[] = []
const dynamicCount = dynamicChildren.reduce( let hasStatic = false
(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) for (let index = 0; index < children.length; index++) {
let anchor: InsertAnchor const child = ctx.dynamic.children[index]
if (index === 0) {
anchor = 'first'
} else if (index === children.length - 1) {
anchor = 'last'
} else {
childrenTemplate[index] = `<!>`
anchor = child.placeholder = ctx.incraseId()
}
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({ ctx.registerOpration({
type: IRNodeTypes.INSERT_NODE, type: IRNodeTypes.APPEND_NODE,
loc: ctx.node.loc, loc: ctx.node.loc,
element: child.id!, elements: prevChildren.map((child) => child.id!),
parent: ctx.reference(), parent: ctx.reference(),
anchor,
}) })
} }
} }

View 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[]) { export function append(parent: ParentBlock, ...nodes: Node[]) {
if (parent instanceof Node) { if (parent instanceof Node) {
parent.append(...nodes) parent.append(...nodes)

View File

@ -0,0 +1,3 @@
<template>
{{ 1 }}{{ 2 }}3{{ 4 }}{{ 5 }}6{{ 7 }}{{ 8 }}9{{ 'A' }}{{ 'B' }}
</template>