From: 三咲智子 Kevin Deng Date: Thu, 1 Feb 2024 11:12:29 +0000 (+0800) Subject: revert: "refactor: id rewrite of vapor v-for" X-Git-Tag: v3.6.0-alpha.1~16^2~611 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e7e3b1c73e1480ce89e662dac2257438c9bdb512;p=thirdparty%2Fvuejs%2Fcore.git revert: "refactor: id rewrite of vapor v-for" This reverts commit 31f497b1d15a1d24a93d7fe91249f00319b5051f. --- diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap index 5b69382d78..ed3f049073 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap @@ -1,31 +1,29 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`compiler: v-for > basic v-for 1`] = ` -"import { template as _template, fragment as _fragment, renderEffect as _renderEffect, children as _children, on as _on, setText as _setText, createFor as _createFor, append as _append } from 'vue/vapor'; +"import { template as _template, fragment as _fragment, children as _children, on as _on, setText as _setText, renderEffect as _renderEffect, createFor as _createFor, append as _append } from 'vue/vapor'; export function render(_ctx) { const t0 = _template("
") const t1 = _fragment() const n0 = t1() const n1 = _createFor(() => (_ctx.items), (_block) => { - let item - _renderEffect(() => { - ([item] = _block.s); - }) const n2 = t0() const { 0: [n3],} = _children(n2) - _on(n3, "click", $event => (_ctx.remove(item))) - _renderEffect(() => { + _on(n3, "click", $event => (_ctx.remove(_block.s[0]))) + const _updateEffect = () => { + const [item] = _block.s _setText(n3, item) - }) - return n2 + } + _renderEffect(_updateEffect) + return [n2, _updateEffect] }) _append(n0, n1) return n0 }" `; -exports[`compiler: v-for > no value 1`] = ` +exports[`compiler: v-for > basic v-for 2`] = ` "import { template as _template, fragment as _fragment, createFor as _createFor, append as _append } from 'vue/vapor'; export function render(_ctx) { @@ -34,37 +32,7 @@ export function render(_ctx) { const n0 = t1() const n1 = _createFor(() => (_ctx.items), (_block) => { const n2 = t0() - return n2 - }) - _append(n0, n1) - return n0 -}" -`; - -exports[`compiler: v-for > object de-structured value 1`] = ` -"import { template as _template, fragment as _fragment, renderEffect as _renderEffect, children as _children, createTextNode as _createTextNode, append as _append, setText as _setText, createFor as _createFor } from 'vue/vapor'; - -export function render(_ctx) { - const t0 = _template("") - const t1 = _fragment() - const n0 = t1() - const n1 = _createFor(() => (_ctx.items), (_block) => { - let id, value - _renderEffect(() => { - ([{ id, value }] = _block.s); - }) - const n2 = t0() - const { 0: [n5],} = _children(n2) - const n3 = _createTextNode() - const n4 = _createTextNode() - _append(n5, n3, n4) - _renderEffect(() => { - _setText(n3, id) - }) - _renderEffect(() => { - _setText(n4, value) - }) - return n2 + return [n2, () => {}] }) _append(n0, n1) return n0 diff --git a/packages/compiler-vapor/__tests__/transforms/vFor.spec.ts b/packages/compiler-vapor/__tests__/transforms/vFor.spec.ts index 5ba78aec55..bfa31ab537 100644 --- a/packages/compiler-vapor/__tests__/transforms/vFor.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vFor.spec.ts @@ -66,15 +66,8 @@ describe('compiler: v-for', () => { expect((ir.operation[0] as ForIRNode).render.effect).lengthOf(1) }) - test('no value', () => { + test('basic v-for', () => { const { code } = compileWithVFor(`
item
`) expect(code).matchSnapshot() }) - - test('object de-structured value', () => { - const { code } = compileWithVFor( - '{{ id }}{{ value }}', - ) - expect(code).matchSnapshot() - }) }) diff --git a/packages/compiler-vapor/src/generate.ts b/packages/compiler-vapor/src/generate.ts index 4355535cf4..1753f163a6 100644 --- a/packages/compiler-vapor/src/generate.ts +++ b/packages/compiler-vapor/src/generate.ts @@ -7,9 +7,9 @@ import { advancePositionWithMutation, locStub, } from '@vue/compiler-dom' -import type { RootIRNode, VaporHelper } from './ir' +import type { IREffect, RootIRNode, VaporHelper } from './ir' import { SourceMapGenerator } from 'source-map-js' -import { extend, isString } from '@vue/shared' +import { extend, isString, remove } from '@vue/shared' import type { ParserPlugin } from '@babel/parser' import { genTemplate } from './generators/template' import { genBlockFunctionContent } from './generators/block' @@ -69,19 +69,22 @@ export class CodegenContext { return `_${name}` } - identifiers: Record = Object.create(null) - withId = (fn: () => T, ids: string[]): T => { + identifiers: Record = Object.create(null) + withId = (fn: () => T, map: Record): T => { const { identifiers } = this + const ids = Object.keys(map) + for (const id of ids) { - if (identifiers[id] === undefined) identifiers[id] = 0 - identifiers[id]!++ + identifiers[id] ||= [] + identifiers[id].unshift(map[id] || id) } const ret = fn() - ids.forEach(id => identifiers[id]!--) + ids.forEach(id => remove(identifiers[id], map[id] || id)) return ret } + genEffect?: (effects: IREffect[]) => CodeFragment[] constructor( public ir: RootIRNode, diff --git a/packages/compiler-vapor/src/generators/block.ts b/packages/compiler-vapor/src/generators/block.ts index 94d0b05549..ddae02f025 100644 --- a/packages/compiler-vapor/src/generators/block.ts +++ b/packages/compiler-vapor/src/generators/block.ts @@ -21,15 +21,13 @@ export function genBlockFunction( oper: BlockFunctionIRNode, context: CodegenContext, args: CodeFragment[] = [], - preamble: CodeFragment[] = [], - returnValue?: CodeFragment[], + returnValue?: () => CodeFragment[], ): CodeFragment[] { return [ '(', ...args, ') => {', INDENT_START, - ...preamble, ...genBlockFunctionContent(oper, context, returnValue), INDENT_END, NEWLINE, @@ -40,7 +38,7 @@ export function genBlockFunction( export function genBlockFunctionContent( ir: BlockFunctionIRNode | RootIRNode, context: CodegenContext, - returnValue?: CodeFragment[], + returnValue?: () => CodeFragment[], ): CodeFragment[] { const { vaporHelper } = context const [frag, push] = buildCodeFragment( @@ -67,7 +65,11 @@ export function genBlockFunctionContent( push(...genOperations(ir.operation, context)) push(...genEffects(ir.effect, context)) - push(NEWLINE, 'return ', ...(returnValue || [`n${ir.dynamic.id}`])) + push( + NEWLINE, + 'return ', + ...(returnValue ? returnValue() : [`n${ir.dynamic.id}`]), + ) return frag } diff --git a/packages/compiler-vapor/src/generators/event.ts b/packages/compiler-vapor/src/generators/event.ts index d2fd8c37da..77a68433eb 100644 --- a/packages/compiler-vapor/src/generators/event.ts +++ b/packages/compiler-vapor/src/generators/event.ts @@ -45,10 +45,9 @@ export function genSetEvent( const hasMultipleStatements = exp.content.includes(`;`) if (isInlineStatement) { - const expr = context.withId( - () => genExpression(exp, context), - ['$event'], - ) + const expr = context.withId(() => genExpression(exp, context), { + $event: null, + }) return [ '$event => ', hasMultipleStatements ? '{' : '(', diff --git a/packages/compiler-vapor/src/generators/expression.ts b/packages/compiler-vapor/src/generators/expression.ts index 79aadf95e6..8cf5f30e77 100644 --- a/packages/compiler-vapor/src/generators/expression.ts +++ b/packages/compiler-vapor/src/generators/expression.ts @@ -88,8 +88,9 @@ function genIdentifier( const { inline, bindingMetadata } = options let name: string | undefined = id - if (identifiers[id]) { - return [id, NewlineType.None, loc] + const idMap = identifiers[id] + if (idMap && idMap.length) { + return [idMap[0], NewlineType.None, loc] } if (inline) { diff --git a/packages/compiler-vapor/src/generators/for.ts b/packages/compiler-vapor/src/generators/for.ts index 507d37054f..9640553e83 100644 --- a/packages/compiler-vapor/src/generators/for.ts +++ b/packages/compiler-vapor/src/generators/for.ts @@ -6,14 +6,11 @@ import { INDENT_END, INDENT_START, NEWLINE, + buildCodeFragment, } from '../generate' -import type { ForIRNode } from '../ir' -import { - NewlineType, - type SimpleExpressionNode, - walkIdentifiers, -} from '@vue/compiler-dom' -import type { ArrowFunctionExpression } from '@babel/types' +import type { ForIRNode, IREffect } from '../ir' +import { genOperations } from './operation' +import { NewlineType } from '@vue/compiler-dom' export function genFor( oper: ForIRNode, @@ -22,63 +19,73 @@ export function genFor( const { call, vaporHelper } = context const { source, value, key, render } = oper + const rawValue = value && value.content const rawKey = key && key.content + const sourceExpr = ['() => (', ...genExpression(source, context), ')'] - const valueIds = value ? extractParams(value) : new Set() - const keyIds = key ? extractParams(key) : new Set() - const ids = [...valueIds, ...keyIds] + let updateFn = '_updateEffect' + context.genEffect = genEffectInFor - let preamble: CodeFragment[] = [] - if (value || rawKey) { - const assignment: CodeFragment[] = ['let ', ids.join(', ')] + const idMap: Record = {} + if (rawValue) idMap[rawValue] = `_block.s[0]` + if (rawKey) idMap[rawKey] = `_block.s[1]` - preamble = [ - NEWLINE, - ...assignment, - NEWLINE, - ...call(vaporHelper('renderEffect'), [ - '() => {', - INDENT_START, - NEWLINE, - '(', - '[', - value && [value.content, NewlineType.None, value.loc], - rawKey && ', ', - rawKey && [rawKey, NewlineType.None, key.loc], - '] = _block.s', - ');', - INDENT_END, - NEWLINE, - '}', - ]), - ] - } + const blockRet = (): CodeFragment[] => [ + `[n${render.dynamic.id!}, ${updateFn}]`, + ] - const blockRet: CodeFragment[] = [`n${render.dynamic.id!}`] const blockFn = context.withId( - () => genBlockFunction(render, context, ['_block'], preamble, blockRet), - ids, + () => genBlockFunction(render, context, ['_block'], blockRet), + idMap, ) + context.genEffect = undefined + return [ NEWLINE, `const n${oper.id} = `, ...call(vaporHelper('createFor'), sourceExpr, blockFn), ] -} -function extractParams(node: SimpleExpressionNode) { - const ids = new Set() - if (node.ast === null || node.ast === false) { - ids.add(node.content) - } else { - walkIdentifiers( - node.ast as ArrowFunctionExpression, - (id, parent, parentStack, isReference, isLocal) => { - if (isLocal) ids.add(id.name) - }, - true, - ) + function genEffectInFor(effects: IREffect[]): CodeFragment[] { + if (!effects.length) { + updateFn = '() => {}' + return [] + } + + const [frag, push] = buildCodeFragment(INDENT_START) + // const [value, key] = _block.s + if (rawValue || rawKey) { + push( + NEWLINE, + 'const ', + '[', + rawValue && [rawValue, NewlineType.None, value.loc], + rawKey && ', ', + rawKey && [rawKey, NewlineType.None, key.loc], + '] = _block.s', + ) + } + + const idMap: Record = {} + if (value) idMap[value.content] = null + if (key) idMap[key.content] = null + context.withId(() => { + effects.forEach(effect => + push(...genOperations(effect.operations, context)), + ) + }, idMap) + + push(INDENT_END) + + return [ + NEWLINE, + `const ${updateFn} = () => {`, + ...frag, + NEWLINE, + '}', + NEWLINE, + `${vaporHelper('renderEffect')}(${updateFn})`, + ] } - return ids } diff --git a/packages/compiler-vapor/src/generators/operation.ts b/packages/compiler-vapor/src/generators/operation.ts index c672281ce7..d1d5575702 100644 --- a/packages/compiler-vapor/src/generators/operation.ts +++ b/packages/compiler-vapor/src/generators/operation.ts @@ -25,7 +25,7 @@ export function genOperations(opers: OperationNode[], context: CodegenContext) { return frag } -export function genOperation( +function genOperation( oper: OperationNode, context: CodegenContext, ): CodeFragment[] { @@ -60,6 +60,9 @@ export function genOperation( } export function genEffects(effects: IREffect[], context: CodegenContext) { + if (context.genEffect) { + return context.genEffect(effects) + } const [frag, push] = buildCodeFragment() for (const effect of effects) { push(...genEffect(effect, context)) @@ -67,7 +70,7 @@ export function genEffects(effects: IREffect[], context: CodegenContext) { return frag } -export function genEffect({ operations }: IREffect, context: CodegenContext) { +function genEffect({ operations }: IREffect, context: CodegenContext) { const { vaporHelper } = context const [frag, push] = buildCodeFragment( NEWLINE, diff --git a/packages/runtime-vapor/src/for.ts b/packages/runtime-vapor/src/for.ts index c486643c83..ed68491ead 100644 --- a/packages/runtime-vapor/src/for.ts +++ b/packages/runtime-vapor/src/for.ts @@ -15,7 +15,7 @@ interface ForBlock extends Fragment { export const createFor = ( src: () => any[] | Record | Set | Map, - renderItem: (block: ForBlock) => Block, + renderItem: (block: ForBlock) => [Block, () => void], getKey: ((item: any, index: number) => any) | null, getMemo?: (item: any) => any[], hydrationNode?: Node, @@ -47,8 +47,9 @@ export const createFor = ( memo: getMemo && getMemo(item), [fragmentKey]: true, }) - block.nodes = scope.run(() => renderItem(block))! - block.update = () => scope.effects.forEach(effect => effect.run()) + const res = scope.run(() => renderItem(block))! + block.nodes = res[0] + block.update = res[1] if (getMemo) block.update() if (parent) insert(block.nodes, parent, anchor) return block