Skip to content

WIP: refining vapor mode #13148

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

Draft
wants to merge 28 commits into
base: vapor
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4972e17
refactor(reactivity): associate effects and effect scopes based on do…
johnsoncodehk Apr 2, 2025
1b0d881
refactor(runtime-core): decouple pre type scheduler job processing
johnsoncodehk Apr 5, 2025
7a907d5
refactor(runtime-vapor): rewrite renderEffect as a class
johnsoncodehk Apr 6, 2025
8178e99
feat(benchmark): add fast-bench script
johnsoncodehk Apr 6, 2025
f0dcbc5
feat(compiler-vapor, runtime-vapor): recognize selector pattern and k…
johnsoncodehk Apr 11, 2025
a893681
refactor(reactivity): execute effect stop in unlink
johnsoncodehk Apr 11, 2025
00b1f66
refactor(runtime-vapor): redo v-for updating
johnsoncodehk Apr 13, 2025
481df0e
refactor: minor code adjustments
johnsoncodehk Apr 15, 2025
ee1a7ab
refactor: make effect scope enable/disable logic symmetric
johnsoncodehk Apr 15, 2025
9311832
refactor(runtime-vapor): move moveOrMount function
johnsoncodehk Apr 15, 2025
151a5da
perf(runtime-vapor): remove reliance on onScopeDispose in useSelector
johnsoncodehk Apr 15, 2025
a78150a
fix(runtime-vapor): setup minimum valid length of pendingNews
johnsoncodehk Apr 16, 2025
8ae2fd4
refactor(reactivity): rename "callback" to "fn" in ReactiveEffect
johnsoncodehk Apr 16, 2025
e704320
refactor(reactivity): remove SubscriberFlags.Propagated
johnsoncodehk Apr 16, 2025
ca05635
refactor(reactivity): do not use effect active getter
johnsoncodehk Apr 16, 2025
16e15c7
perf(runtime-vapor): fast path to replace all rows
johnsoncodehk Apr 16, 2025
accf4b3
refactor(reactivity): simplify Effect, EffectScope cleanup behavior
johnsoncodehk Apr 17, 2025
8ac96e4
[autofix.ci] apply automated fixes
autofix-ci[bot] Apr 17, 2025
4d4fc7b
Revert "feat(benchmark): add fast-bench script"
johnsoncodehk Apr 17, 2025
403fb25
refactor(reactivity): optimize dirty flag checks
johnsoncodehk Apr 18, 2025
ba337af
refactor(runtime-vapor): streamline update job handling in RenderEffect
johnsoncodehk Apr 18, 2025
59b1da8
wip
johnsoncodehk Apr 20, 2025
0441a11
wip
johnsoncodehk Apr 21, 2025
f41ac33
wip
johnsoncodehk Apr 21, 2025
79f7e85
wip
johnsoncodehk Apr 21, 2025
dc438a8
wip
johnsoncodehk Apr 21, 2025
57d428c
wip
johnsoncodehk Apr 21, 2025
3305c45
wip
johnsoncodehk Apr 21, 2025
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[],
shouldDeclareConst: 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,
shouldDeclareConst,
)
}

function analyzeExpressions(expressions: SimpleExpressionNode[]) {
Expand Down Expand Up @@ -507,31 +513,41 @@ function processRepeatedExpressions(
function genDeclarations(
declarations: DeclarationValue[],
context: CodegenContext,
shouldDeclareConst: 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 (shouldDeclareConst) {
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 (shouldDeclareConst) {
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