feat: append & prepend multiple elements
This commit is contained in:
parent
6ff8b1bf0d
commit
71cf732d6d
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
})
|
})
|
||||||
|
|
|
@ -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()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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}: [`
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
<template>
|
||||||
|
{{ 1 }}{{ 2 }}3{{ 4 }}{{ 5 }}6{{ 7 }}{{ 8 }}9{{ 'A' }}{{ 'B' }}
|
||||||
|
</template>
|
Loading…
Reference in New Issue