refactor(vapor): use bitwise flags for v-for runtime optimizations

This commit is contained in:
Evan You 2025-02-12 15:28:10 +08:00
parent 63cf2ee3f1
commit 242cc15fa6
No known key found for this signature in database
GPG Key ID: 00E9AB7A6704CE0A
6 changed files with 45 additions and 14 deletions

View File

@ -23,7 +23,7 @@ export function render(_ctx) {
const n2 = t0()
_setTemplateRef(n2, "foo", void 0, true)
return n2
}, null, null, true)
}, null, 4)
return n0
}"
`;

View File

@ -77,7 +77,7 @@ export function render(_ctx) {
const x4 = _child(n4)
_renderEffect(() => _setText(x4, _toDisplayString(_for_item1.value+_for_item0.value)))
return n4
}, null, null, null, true)
}, null, 1)
_insert(n2, n5)
return n5
})

View File

@ -68,7 +68,7 @@ export function render(_ctx) {
const n0 = _createFor(() => (_ctx.list), (_for_item0) => {
const n2 = t0()
return n2
}, null, null, true)
}, null, 4)
return n0
}"
`;

View File

@ -10,6 +10,7 @@ import type { ForIRNode } from '../ir'
import { type CodeFragment, NEWLINE, genCall, genMulti } from './utils'
import type { Identifier } from '@babel/types'
import { parseExpression } from '@babel/parser'
import { VaporVForFlags } from '../../../shared/src/vaporFlags'
export function genFor(
oper: ForIRNode,
@ -80,6 +81,17 @@ export function genFor(
const blockFn = context.withId(() => genBlock(render, context, args), idMap)
exitScope()
let flags = 0
if (onlyChild) {
flags |= VaporVForFlags.FAST_REMOVE
}
if (component) {
flags |= VaporVForFlags.IS_COMPONENT
}
if (once) {
flags |= VaporVForFlags.ONCE
}
return [
NEWLINE,
`const n${id} = `,
@ -88,9 +100,7 @@ export function genFor(
sourceExpr,
blockFn,
genCallback(keyProp),
component && 'true',
once && 'true',
onlyChild && `true`,
flags ? String(flags) : undefined,
// todo: hydrationNode
),
]

View File

@ -21,6 +21,7 @@ import { warn } from '@vue/runtime-dom'
import { currentInstance, isVaporComponent } from './component'
import type { DynamicSlot } from './componentSlots'
import { renderEffect } from './renderEffect'
import { VaporVForFlags } from '../../shared/src/vaporFlags'
class ForBlock extends VaporFragment {
scope: EffectScope | undefined
@ -64,13 +65,7 @@ export const createFor = (
index: ShallowRef<number | undefined>,
) => Block,
getKey?: (item: any, key: any, index?: number) => any,
/**
* Whether this v-for is used directly on a component. If true, we can avoid
* creating an extra fragment / scope for each block
*/
isComponent = false,
once?: boolean,
canUseFastRemove?: boolean,
flags = 0,
// hydrationNode?: Node,
): VaporFragment => {
let isMounted = false
@ -80,6 +75,8 @@ export const createFor = (
const parentAnchor = __DEV__ ? createComment('for') : createTextNode()
const frag = new VaporFragment(oldBlocks)
const instance = currentInstance!
const canUseFastRemove = flags & VaporVForFlags.FAST_REMOVE
const isComponent = flags & VaporVForFlags.IS_COMPONENT
if (__DEV__ && !instance) {
warn('createFor() can only be used inside setup()')
@ -354,7 +351,11 @@ export const createFor = (
doRemove && removeBlock(nodes, parent!)
}
once ? renderList() : renderEffect(renderList)
if (flags & VaporVForFlags.ONCE) {
renderList()
} else {
renderEffect(renderList)
}
return frag
}

View File

@ -0,0 +1,20 @@
/**
* Flags to optimize vapor `createFor` runtime behavior, shared between the
* compiler and the runtime
*/
export enum VaporVForFlags {
/**
* v-for is the only child of a parent container, so it can take the fast
* path with textContent = '' when the whole list is emptied
*/
FAST_REMOVE = 1,
/**
* v-for used on component - we can skip creating child scopes for each block
* because the component itself already has a scope.
*/
IS_COMPONENT = 1 << 1,
/**
* v-for inside v-ince
*/
ONCE = 1 << 2,
}