Skip to content

feat(vapor): generate more efficient runtime code for specific interpolation patterns #13278

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: vapor
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,27 @@ export function render(_ctx) {
}"
`;

exports[`compiler: v-for > key only binding pattern 1`] = `
"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, createFor as _createFor, template as _template } from 'vue';
const t0 = _template("<tr> </tr>", true)

export function render(_ctx) {
const n0 = _createFor(() => (_ctx.rows), (_for_item0) => {
const n2 = t0()
const x2 = _child(n2)
let _row, _row_id
{
_row = _for_item0.value
_row_id = _row.id

}
_setText(x2, _toDisplayString(_row_id + _row_id))
return n2
}, (row) => (row.id))
return n0
}"
`;

exports[`compiler: v-for > multi effect 1`] = `
"import { setProp as _setProp, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
const t0 = _template("<div></div>", true)
Expand Down Expand Up @@ -115,6 +136,75 @@ export function render(_ctx) {
}"
`;

exports[`compiler: v-for > selector pattern 1`] = `
"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, createFor as _createFor, template as _template } from 'vue';
const t0 = _template("<tr> </tr>", true)

export function render(_ctx) {
const n0 = _createFor(() => (_ctx.rows), (_for_item0) => {
const n2 = t0()
const x2 = _child(n2)
_selector0_0(() => {
_setText(x2, _toDisplayString(_ctx.selected === _for_item0.value.id ? 'danger' : ''))
})
return n2
}, (row) => (row.id))
const _selector0_0 = n0.useSelector(() => _ctx.selected)
return n0
}"
`;

exports[`compiler: v-for > selector pattern 2`] = `
"import { setClass as _setClass, createFor as _createFor, template as _template } from 'vue';
const t0 = _template("<tr></tr>", true)

export function render(_ctx) {
const n0 = _createFor(() => (_ctx.rows), (_for_item0) => {
const n2 = t0()
_selector0_0(() => {
_setClass(n2, _ctx.selected === _for_item0.value.id ? 'danger' : '')
})
return n2
}, (row) => (row.id))
const _selector0_0 = n0.useSelector(() => _ctx.selected)
return n0
}"
`;

exports[`compiler: v-for > selector pattern 3`] = `
"import { setClass as _setClass, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
const t0 = _template("<tr></tr>", true)

export function render(_ctx) {
const n0 = _createFor(() => (_ctx.rows), (_for_item0) => {
const n2 = t0()
_renderEffect(() => {
const _row = _for_item0.value
_setClass(n2, _row.label === _row.id ? 'danger' : '')
})
return n2
}, (row) => (row.id))
return n0
}"
`;

exports[`compiler: v-for > selector pattern 4`] = `
"import { setClass as _setClass, createFor as _createFor, template as _template } from 'vue';
const t0 = _template("<tr></tr>", true)

export function render(_ctx) {
const n0 = _createFor(() => (_ctx.rows), (_for_item0) => {
const n2 = t0()
_selector0_0(() => {
_setClass(n2, { danger: _for_item0.value.id === _ctx.selected })
})
return n2
}, (row) => (row.id))
const _selector0_0 = n0.useSelector(() => _ctx.selected)
return n0
}"
`;

exports[`compiler: v-for > v-for aliases w/ complex expressions 1`] = `
"import { getDefaultValue as _getDefaultValue, child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, template as _template } from 'vue';
const t0 = _template("<div> </div>", true)
Expand Down
67 changes: 67 additions & 0 deletions packages/compiler-vapor/__tests__/transforms/vFor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,73 @@ describe('compiler: v-for', () => {
).lengthOf(1)
})

test('key only binding pattern', () => {
expect(
compileWithVFor(
`
<tr
v-for="row of rows"
:key="row.id"
>
{{ row.id + row.id }}
</tr>
`,
).code,
).matchSnapshot()
})

test('selector pattern', () => {
expect(
compileWithVFor(
`
<tr
v-for="row of rows"
:key="row.id"
>
{{ selected === row.id ? 'danger' : '' }}
</tr>
`,
).code,
).matchSnapshot()

expect(
compileWithVFor(
`
<tr
v-for="row of rows"
:key="row.id"
:class="selected === row.id ? 'danger' : ''"
></tr>
`,
).code,
).matchSnapshot()

// Should not be optimized because row.label is not from parent scope
expect(
compileWithVFor(
`
<tr
v-for="row of rows"
:key="row.id"
:class="row.label === row.id ? 'danger' : ''"
></tr>
`,
).code,
).matchSnapshot()

expect(
compileWithVFor(
`
<tr
v-for="row of rows"
:key="row.id"
:class="{ danger: row.id === selected }"
></tr>
`,
).code,
).matchSnapshot()
})

test('multi effect', () => {
const { code } = compileWithVFor(
`<div v-for="(item, index) of items" :item="item" :index="index" />`,
Expand Down
9 changes: 4 additions & 5 deletions packages/compiler-vapor/src/generators/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,13 @@ export function genBlock(
context: CodegenContext,
args: CodeFragment[] = [],
root?: boolean,
customReturns?: (returns: CodeFragment[]) => CodeFragment[],
): CodeFragment[] {
return [
'(',
...args,
') => {',
INDENT_START,
...genBlockContent(oper, context, root, customReturns),
...genBlockContent(oper, context, root),
INDENT_END,
NEWLINE,
'}',
Expand All @@ -37,7 +36,7 @@ export function genBlockContent(
block: BlockIRNode,
context: CodegenContext,
root?: boolean,
customReturns?: (returns: CodeFragment[]) => CodeFragment[],
genEffectsExtraFrag?: () => CodeFragment[],
): CodeFragment[] {
const [frag, push] = buildCodeFragment()
const { dynamic, effect, operation, returns } = block
Expand All @@ -56,7 +55,7 @@ export function genBlockContent(
}

push(...genOperations(operation, context))
push(...genEffects(effect, context))
push(...genEffects(effect, context, genEffectsExtraFrag))

push(NEWLINE, `return `)

Expand All @@ -65,7 +64,7 @@ export function genBlockContent(
returnNodes.length > 1
? genMulti(DELIMITERS_ARRAY, ...returnNodes)
: [returnNodes[0] || 'null']
push(...(customReturns ? customReturns(returnsCode) : returnsCode))
push(...returnsCode)

resetBlock()
return frag
Expand Down
24 changes: 20 additions & 4 deletions packages/compiler-vapor/src/generators/expression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ function canPrefix(name: string) {
type DeclarationResult = {
ids: Record<string, string>
frag: CodeFragment[]
varNames: string[]
}
type DeclarationValue = {
name: string
Expand All @@ -243,6 +244,7 @@ type DeclarationValue = {
export function processExpressions(
context: CodegenContext,
expressions: SimpleExpressionNode[],
shouldDeclare: boolean,
): DeclarationResult {
// analyze variables
const { seenVariable, variableToExpMap, expToVariableMap, seenIdentifier } =
Expand All @@ -266,7 +268,11 @@ export function processExpressions(
varDeclarations,
)

return genDeclarations([...varDeclarations, ...expDeclarations], context)
return genDeclarations(
[...varDeclarations, ...expDeclarations],
context,
shouldDeclare,
)
}

function analyzeExpressions(expressions: SimpleExpressionNode[]) {
Expand Down Expand Up @@ -507,31 +513,41 @@ function processRepeatedExpressions(
function genDeclarations(
declarations: DeclarationValue[],
context: CodegenContext,
shouldDeclare: boolean,
): DeclarationResult {
const [frag, push] = buildCodeFragment()
const ids: Record<string, string> = Object.create(null)
const varNames = new Set<string>()

// process identifiers first as expressions may rely on them
declarations.forEach(({ name, isIdentifier, value }) => {
if (isIdentifier) {
const varName = (ids[name] = `_${name}`)
push(`const ${varName} = `, ...genExpression(value, context), NEWLINE)
varNames.add(varName)
if (shouldDeclare) {
push(`const `)
}
push(`${varName} = `, ...genExpression(value, context), NEWLINE)
}
})

// process expressions
declarations.forEach(({ name, isIdentifier, value }) => {
if (!isIdentifier) {
const varName = (ids[name] = `_${name}`)
varNames.add(varName)
if (shouldDeclare) {
push(`const `)
}
push(
`const ${varName} = `,
`${varName} = `,
...context.withId(() => genExpression(value, context), ids),
NEWLINE,
)
}
})

return { ids, frag }
return { ids, frag, varNames: [...varNames] }
}

function escapeRegExp(string: string) {
Expand Down
Loading