refactor(compiler-vapor): remove runtime fragment

returns array directly
This commit is contained in:
三咲智子 Kevin Deng 2024-02-07 17:29:04 +08:00
parent 823e47ca62
commit 99da2e5abe
No known key found for this signature in database
GPG Key ID: 69992F2250DFD93E
12 changed files with 116 additions and 202 deletions

View File

@ -172,16 +172,12 @@ export function render(_ctx) {
`;
exports[`compile > dynamic root 1`] = `
"import { fragment as _fragment, createTextNode as _createTextNode, append as _append, renderEffect as _renderEffect, setText as _setText } from 'vue/vapor';
const t0 = _fragment()
"import { createTextNode as _createTextNode, renderEffect as _renderEffect, setText as _setText } from 'vue/vapor';
export function render(_ctx) {
const n0 = t0()
const n1 = _createTextNode()
_append(n0, n1)
_renderEffect(() => _setText(n1, 1, 2))
return n0
return [n1]
}"
`;
@ -201,14 +197,10 @@ export function render(_ctx) {
`;
exports[`compile > expression parsing > interpolation 1`] = `
"
const t0 = _fragment()
(() => {
const n0 = t0()
"(() => {
const n1 = _createTextNode()
_append(n0, n1)
_renderEffect(() => _setText(n1, a + b.value))
return n0
return [n1]
})()"
`;
@ -235,16 +227,12 @@ export function render(_ctx) {
`;
exports[`compile > static + dynamic root 1`] = `
"import { fragment as _fragment, createTextNode as _createTextNode, append as _append, renderEffect as _renderEffect, setText as _setText } from 'vue/vapor';
const t0 = _fragment()
"import { createTextNode as _createTextNode, renderEffect as _renderEffect, setText as _setText } from 'vue/vapor';
export function render(_ctx) {
const n0 = t0()
const n1 = _createTextNode()
_append(n0, n1)
_renderEffect(() => _setText(n1, 1, 2, "3", 4, 5, "6", 7, 8, "9", 'A', 'B'))
return n0
return [n1]
}"
`;

View File

@ -1,13 +1,11 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`compiler: v-for > basic v-for 1`] = `
"import { template as _template, fragment as _fragment, children as _children, on as _on, renderEffect as _renderEffect, setText as _setText, createFor as _createFor, append as _append } from 'vue/vapor';
"import { template as _template, children as _children, on as _on, renderEffect as _renderEffect, setText as _setText, createFor as _createFor } from 'vue/vapor';
const t0 = _template("<div></div>")
const t1 = _fragment()
export function render(_ctx) {
const n0 = t1()
const n1 = _createFor(() => (_ctx.items), (_block) => {
const n2 = t0()
const { 0: [n3],} = _children(n2)
@ -18,19 +16,16 @@ export function render(_ctx) {
})
return n2
})
_append(n0, n1)
return n0
return [n1]
}"
`;
exports[`compiler: v-for > multi effect 1`] = `
"import { template as _template, fragment as _fragment, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, createFor as _createFor, append as _append } from 'vue/vapor';
"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, createFor as _createFor } from 'vue/vapor';
const t0 = _template("<div></div>")
const t1 = _fragment()
export function render(_ctx) {
const n0 = t1()
const n1 = _createFor(() => (_ctx.items), (_block) => {
const n2 = t0()
const { 0: [n3],} = _children(n2)
@ -44,24 +39,20 @@ export function render(_ctx) {
})
return n2
})
_append(n0, n1)
return n0
return [n1]
}"
`;
exports[`compiler: v-for > w/o value 1`] = `
"import { template as _template, fragment as _fragment, createFor as _createFor, append as _append } from 'vue/vapor';
"import { template as _template, createFor as _createFor } from 'vue/vapor';
const t0 = _template("<div>item</div>")
const t1 = _fragment()
export function render(_ctx) {
const n0 = t1()
const n1 = _createFor(() => (_ctx.items), (_block) => {
const n2 = t0()
return n2
})
_append(n0, n1)
return n0
return [n1]
}"
`;

View File

@ -1,21 +1,18 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`compiler: v-if > basic v-if 1`] = `
"import { template as _template, fragment as _fragment, children as _children, renderEffect as _renderEffect, setText as _setText, createIf as _createIf, append as _append } from 'vue/vapor';
"import { template as _template, children as _children, renderEffect as _renderEffect, setText as _setText, createIf as _createIf } from 'vue/vapor';
const t0 = _template("<div></div>")
const t1 = _fragment()
export function render(_ctx) {
const n0 = t1()
const n1 = _createIf(() => (_ctx.ok), () => {
const n2 = t0()
const { 0: [n3],} = _children(n2)
_renderEffect(() => _setText(n3, _ctx.msg))
return n2
})
_append(n0, n1)
return n0
return [n1]
}"
`;
@ -47,13 +44,11 @@ export function render(_ctx) {
`;
exports[`compiler: v-if > dedupe same template 1`] = `
"import { template as _template, fragment as _fragment, createIf as _createIf, append as _append } from 'vue/vapor';
"import { template as _template, createIf as _createIf } from 'vue/vapor';
const t0 = _template("<div>hello</div>")
const t1 = _fragment()
export function render(_ctx) {
const n0 = t1()
const n1 = _createIf(() => (_ctx.ok), () => {
const n2 = t0()
return n2
@ -62,39 +57,33 @@ export function render(_ctx) {
const n4 = t0()
return n4
})
_append(n0, n1, n3)
return n0
return [n1, n3]
}"
`;
exports[`compiler: v-if > template v-if 1`] = `
"import { template as _template, fragment as _fragment, children as _children, renderEffect as _renderEffect, setText as _setText, createIf as _createIf, append as _append } from 'vue/vapor';
"import { template as _template, children as _children, renderEffect as _renderEffect, setText as _setText, createIf as _createIf } from 'vue/vapor';
const t0 = _template("<div></div>hello<p></p>")
const t1 = _fragment()
export function render(_ctx) {
const n0 = t1()
const n1 = _createIf(() => (_ctx.ok), () => {
const n2 = t0()
const { 2: [n3],} = _children(n2)
_renderEffect(() => _setText(n3, _ctx.msg))
return n2
})
_append(n0, n1)
return n0
return [n1]
}"
`;
exports[`compiler: v-if > v-if + v-else 1`] = `
"import { template as _template, fragment as _fragment, createIf as _createIf, append as _append } from 'vue/vapor';
"import { template as _template, createIf as _createIf } from 'vue/vapor';
const t0 = _template("<div></div>")
const t1 = _template("<p></p>")
const t2 = _fragment()
export function render(_ctx) {
const n0 = t2()
const n1 = _createIf(() => (_ctx.ok), () => {
const n2 = t0()
return n2
@ -102,21 +91,18 @@ export function render(_ctx) {
const n3 = t1()
return n3
})
_append(n0, n1)
return n0
return [n1]
}"
`;
exports[`compiler: v-if > v-if + v-else-if + v-else 1`] = `
"import { template as _template, fragment as _fragment, createIf as _createIf, append as _append } from 'vue/vapor';
"import { template as _template, createIf as _createIf } from 'vue/vapor';
const t0 = _template("<div></div>")
const t1 = _template("<p></p>")
const t2 = _template("fine")
const t3 = _fragment()
export function render(_ctx) {
const n0 = t3()
const n1 = _createIf(() => (_ctx.ok), () => {
const n2 = t0()
return n2
@ -127,20 +113,17 @@ export function render(_ctx) {
const n4 = t2()
return n4
}))
_append(n0, n1)
return n0
return [n1]
}"
`;
exports[`compiler: v-if > v-if + v-else-if 1`] = `
"import { template as _template, fragment as _fragment, createIf as _createIf, append as _append } from 'vue/vapor';
"import { template as _template, createIf as _createIf } from 'vue/vapor';
const t0 = _template("<div></div>")
const t1 = _template("<p></p>")
const t2 = _fragment()
export function render(_ctx) {
const n0 = t2()
const n1 = _createIf(() => (_ctx.ok), () => {
const n2 = t0()
return n2
@ -148,7 +131,6 @@ export function render(_ctx) {
const n3 = t1()
return n3
}))
_append(n0, n1)
return n0
return [n1]
}"
`;

View File

@ -27,15 +27,12 @@ describe('compiler: v-for', () => {
expect(code).matchSnapshot()
expect(vaporHelpers).contains('createFor')
expect(helpers.size).toBe(0)
expect(ir.template).lengthOf(2)
expect(ir.template).lengthOf(1)
expect(ir.template).toMatchObject([
{
template: '<div></div>',
type: IRNodeTypes.TEMPLATE_FACTORY,
},
{
type: IRNodeTypes.FRAGMENT_FACTORY,
},
])
expect(ir.operation).toMatchObject([
{
@ -56,12 +53,8 @@ describe('compiler: v-for', () => {
templateIndex: 0,
},
},
{
type: IRNodeTypes.APPEND_NODE,
elements: [1],
parent: 0,
},
])
expect(ir.returns).toEqual([1])
expect(ir.dynamic).toMatchObject({
id: 0,
children: { 0: { id: 1 } },

View File

@ -31,15 +31,12 @@ describe('compiler: v-if', () => {
expect(vaporHelpers).contains('createIf')
expect(helpers.size).toBe(0)
expect(ir.template).lengthOf(2)
expect(ir.template).lengthOf(1)
expect(ir.template).toMatchObject([
{
template: '<div></div>',
type: IRNodeTypes.TEMPLATE_FACTORY,
},
{
type: IRNodeTypes.FRAGMENT_FACTORY,
},
])
expect(ir.operation).toMatchObject([
{
@ -55,12 +52,8 @@ describe('compiler: v-if', () => {
templateIndex: 0,
},
},
{
type: IRNodeTypes.APPEND_NODE,
elements: [1],
parent: 0,
},
])
expect(ir.returns).toEqual([1])
expect(ir.dynamic).toMatchObject({
id: 0,
@ -79,11 +72,13 @@ describe('compiler: v-if', () => {
)
expect(code).matchSnapshot()
expect(ir.template).lengthOf(2)
expect(ir.template[0]).toMatchObject({
template: '<div></div>hello<p></p>',
type: IRNodeTypes.TEMPLATE_FACTORY,
})
expect(ir.template).lengthOf(1)
expect(ir.template).toMatchObject([
{
template: '<div></div>hello<p></p>',
type: IRNodeTypes.TEMPLATE_FACTORY,
},
])
expect(ir.effect).toEqual([])
expect((ir.operation[0] as IfIRNode).positive.effect).toMatchObject([
@ -114,7 +109,14 @@ describe('compiler: v-if', () => {
`<div v-if="ok">hello</div><div v-if="ok">hello</div>`,
)
expect(code).matchSnapshot()
expect(ir.template).lengthOf(2)
expect(ir.template).lengthOf(1)
expect(ir.template).toMatchObject([
{
template: '<div>hello</div>',
type: 2,
},
])
expect(ir.returns).toEqual([1, 3])
})
test.todo('v-if with v-once')
@ -125,7 +127,7 @@ describe('compiler: v-if', () => {
`<div v-if="ok"/><p v-else/>`,
)
expect(code).matchSnapshot()
expect(ir.template).lengthOf(3)
expect(ir.template).lengthOf(2)
expect(ir.template).toMatchObject([
{
template: '<div></div>',
@ -135,9 +137,6 @@ describe('compiler: v-if', () => {
template: '<p></p>',
type: IRNodeTypes.TEMPLATE_FACTORY,
},
{
type: IRNodeTypes.FRAGMENT_FACTORY,
},
])
expect(vaporHelpers).contains('createIf')
@ -161,12 +160,8 @@ describe('compiler: v-if', () => {
templateIndex: 1,
},
},
{
type: IRNodeTypes.APPEND_NODE,
elements: [1],
parent: 0,
},
])
expect(ir.returns).toEqual([1])
})
test('v-if + v-else-if', () => {
@ -174,7 +169,7 @@ describe('compiler: v-if', () => {
`<div v-if="ok"/><p v-else-if="orNot"/>`,
)
expect(code).matchSnapshot()
expect(ir.template).lengthOf(3)
expect(ir.template).lengthOf(2)
expect(ir.template).toMatchObject([
{
template: '<div></div>',
@ -184,7 +179,6 @@ describe('compiler: v-if', () => {
template: '<p></p>',
type: IRNodeTypes.TEMPLATE_FACTORY,
},
{ type: IRNodeTypes.FRAGMENT_FACTORY },
])
expect(ir.operation).toMatchObject([
@ -213,12 +207,8 @@ describe('compiler: v-if', () => {
},
},
},
{
type: IRNodeTypes.APPEND_NODE,
elements: [1],
parent: 0,
},
])
expect(ir.returns).toEqual([1])
})
test('v-if + v-else-if + v-else', () => {
@ -226,7 +216,7 @@ describe('compiler: v-if', () => {
`<div v-if="ok"/><p v-else-if="orNot"/><template v-else>fine</template>`,
)
expect(code).matchSnapshot()
expect(ir.template).lengthOf(4)
expect(ir.template).lengthOf(3)
expect(ir.template).toMatchObject([
{
template: '<div></div>',
@ -240,9 +230,9 @@ describe('compiler: v-if', () => {
template: 'fine',
type: IRNodeTypes.TEMPLATE_FACTORY,
},
{ type: IRNodeTypes.FRAGMENT_FACTORY },
])
expect(ir.returns).toEqual([1])
expect(ir.operation).toMatchObject([
{
type: IRNodeTypes.IF,
@ -263,11 +253,6 @@ describe('compiler: v-if', () => {
},
},
},
{
type: IRNodeTypes.APPEND_NODE,
elements: [1],
parent: 0,
},
])
})

View File

@ -38,11 +38,12 @@ export function genBlockFunctionContent(
ir: BlockFunctionIRNode | RootIRNode,
context: CodegenContext,
): CodeFragment[] {
const { vaporHelper } = context
const [frag, push] = buildCodeFragment(
NEWLINE,
`const n${ir.dynamic.id} = t${ir.templateIndex}()`,
)
const { vaporHelper, multi } = context
const [frag, push] = buildCodeFragment()
if (ir.templateIndex > -1) {
push(NEWLINE, `const n${ir.dynamic.id} = t${ir.templateIndex}()`)
}
const children = genChildren(ir.dynamic.children)
if (children) {
@ -62,7 +63,15 @@ export function genBlockFunctionContent(
push(...genOperations(ir.operation, context))
push(...(context.genEffect || genEffects)(ir.effect, context))
push(NEWLINE, `return n${ir.dynamic.id}`)
if (ir.returns) {
push(
NEWLINE,
`return `,
...multi(['[', ']', ', '], ...ir.returns.map(n => `n${n}`)),
)
} else {
push(NEWLINE, `return n${ir.dynamic.id}`)
}
return frag
}

View File

@ -1,25 +1,16 @@
import { type CodeFragment, type CodegenContext, NEWLINE } from '../generate'
import {
type FragmentFactoryIRNode,
IRNodeTypes,
type TemplateFactoryIRNode,
} from '../ir'
import type { TemplateFactoryIRNode } from '../ir'
export function genTemplate(
node: TemplateFactoryIRNode | FragmentFactoryIRNode,
node: TemplateFactoryIRNode,
index: number,
{ vaporHelper }: CodegenContext,
): CodeFragment[] {
if (node.type === IRNodeTypes.TEMPLATE_FACTORY) {
// TODO source map?
return [
NEWLINE,
`const t${index} = ${vaporHelper('template')}(${JSON.stringify(
node.template,
)})`,
]
} else {
// fragment
return [NEWLINE, `const t${index} = ${vaporHelper('fragment')}()`]
}
// TODO source map?
return [
NEWLINE,
`const t${index} = ${vaporHelper('template')}(${JSON.stringify(
node.template,
)})`,
]
}

View File

@ -18,7 +18,6 @@ export enum IRNodeTypes {
BLOCK_FUNCTION,
TEMPLATE_FACTORY,
FRAGMENT_FACTORY,
SET_PROP,
SET_DYNAMIC_PROPS,
@ -52,13 +51,14 @@ export interface BlockFunctionIRNode extends BaseIRNode {
dynamic: IRDynamicInfo
effect: IREffect[]
operation: OperationNode[]
returns?: number[]
}
export interface RootIRNode extends Omit<BlockFunctionIRNode, 'type'> {
type: IRNodeTypes.ROOT
node: RootNode
source: string
template: Array<TemplateFactoryIRNode | FragmentFactoryIRNode>
template: Array<TemplateFactoryIRNode>
}
export interface IfIRNode extends BaseIRNode {
@ -84,10 +84,6 @@ export interface TemplateFactoryIRNode extends BaseIRNode {
template: string
}
export interface FragmentFactoryIRNode extends BaseIRNode {
type: IRNodeTypes.FRAGMENT_FACTORY
}
export interface IRProp extends Omit<DirectiveTransformResult, 'value'> {
values: SimpleExpressionNode[]
}
@ -180,11 +176,7 @@ export interface WithDirectiveIRNode extends BaseIRNode {
builtin?: VaporHelper
}
export type IRNode =
| OperationNode
| RootIRNode
| TemplateFactoryIRNode
| FragmentFactoryIRNode
export type IRNode = OperationNode | RootIRNode | TemplateFactoryIRNode
export type OperationNode =
| SetPropIRNode
| SetDynamicPropsIRNode

View File

@ -19,14 +19,12 @@ import { EMPTY_OBJ, NOOP, extend, isArray, isString } from '@vue/shared'
import {
type BlockIRNode,
DynamicFlag,
type FragmentFactoryIRNode,
type HackOptions,
type IRDynamicInfo,
type IRExpression,
IRNodeTypes,
type OperationNode,
type RootIRNode,
type TemplateFactoryIRNode,
type VaporDirectiveNode,
} from './ir'
@ -191,29 +189,21 @@ function createRootContext(
childrenTemplate: [],
registerTemplate() {
this.template += this.childrenTemplate.filter(Boolean).join('')
let templateNode: TemplateFactoryIRNode | FragmentFactoryIRNode
if (!this.template) {
return -1
}
const existing = root.template.findIndex(t =>
this.template
? t.type === IRNodeTypes.TEMPLATE_FACTORY &&
t.template === this.template
: t.type === IRNodeTypes.FRAGMENT_FACTORY,
const existing = root.template.findIndex(
t => t.template === this.template,
)
if (existing !== -1) {
return (this.block.templateIndex = existing)
}
if (this.template) {
templateNode = {
type: IRNodeTypes.TEMPLATE_FACTORY,
template: this.template,
}
} else {
templateNode = {
type: IRNodeTypes.FRAGMENT_FACTORY,
}
}
root.template.push(templateNode)
root.template.push({
type: IRNodeTypes.TEMPLATE_FACTORY,
template: this.template,
})
return (this.block.templateIndex = root.template.length - 1)
},
registerOperation(...node) {
@ -334,8 +324,22 @@ function processDynamicChildren(
) {
let prevDynamics: IRDynamicInfo[] = []
let hasStaticTemplate = false
const children = context.dynamic.children
for (const [index, child] of context.dynamic.children.entries()) {
const isFragment = context.block.node === context.node
const allNonTemplate = children.every(
child => child.flags & DynamicFlag.NON_TEMPLATE,
)
// all non-template: don't gen fragment but return array directly
if (isFragment && allNonTemplate) {
context.block.returns = children
.filter(child => child.flags & DynamicFlag.INSERT)
.map(child => child.id!)
return
}
// mixed: insert with anchor
for (const [index, child] of children.entries()) {
if (child.flags & DynamicFlag.INSERT) {
prevDynamics.push(child)
}

View File

@ -3,7 +3,6 @@ import {
append,
children,
createIf,
fragment,
insert,
nextTick,
ref,
@ -116,31 +115,27 @@ describe('createIf', () => {
const t0 = template('Vapor')
const t1 = template('Hello ')
const t2 = fragment()
render(
defineComponent({
setup() {
// render
return (() => {
const n0 = t2()
append(
n0,
createIf(
() => ok1.value,
() => {
const n2 = t1()
append(
n2,
createIf(
() => ok2.value,
() => t0(),
),
)
return n2
},
),
const n1 = createIf(
() => ok1.value,
() => {
const n2 = t1()
const n3 = createIf(
() => ok2.value,
() => {
const n4 = t0()
return n4
},
)
append(n2, n3)
return n2
},
)
return n0
return [n1]
})()
},
}) as any,

View File

@ -1,4 +1,4 @@
import { fragment, template } from '../src'
import { template } from '../src'
describe('api: template', () => {
test('create element', () => {
@ -11,16 +11,4 @@ describe('api: template', () => {
expect(root2).toBeInstanceOf(Array)
expect(root2).not.toBe(root)
})
test('create fragment', () => {
const frag = fragment()
const root = frag()
expect(root).toBeInstanceOf(Array)
expect(root.length).toBe(0)
const root2 = frag()
expect(root2).toBeInstanceOf(Array)
expect(root2).not.toBe(root)
})
})

View File

@ -23,7 +23,3 @@ export function template(str: string): () => ChildNode[] {
function fragmentToNodes(node: DocumentFragment): ChildNode[] {
return Array.from((node.cloneNode(true) as DocumentFragment).childNodes)
}
export function fragment(): () => ChildNode[] {
return () => []
}