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`] = `
"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;
}

View 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)
})

View 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()
})

View 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}: [`

View 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 {

View 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
let prevChildren: DynamicInfo[] = []
let hasStatic = false
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()
}
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,
})
}
}

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[]) {
if (parent instanceof Node) {
parent.append(...nodes)

View File

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