From e2e009c284b7e274b6b945f6d517b995ef8f91fc Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Sun, 18 May 2025 15:17:15 +0800 Subject: [PATCH 1/6] fix(compiler-vapor): correct execution order of operations --- .../__snapshots__/compile.spec.ts.snap | 4 +- .../transformChildren.spec.ts.snap | 1 - .../__snapshots__/vBind.spec.ts.snap | 13 +--- .../src/generators/operation.ts | 6 +- packages/compiler-vapor/src/ir/index.ts | 1 - packages/compiler-vapor/src/transform.ts | 28 ++------ .../src/transforms/transformElement.ts | 67 +++++++++++++------ .../compiler-vapor/src/transforms/utils.ts | 1 - 8 files changed, 59 insertions(+), 62 deletions(-) diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap index e56676d8706..64dd32b6e75 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap @@ -149,7 +149,7 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { `; exports[`compile > directives > v-pre > should not affect siblings after it 1`] = ` -"import { resolveComponent as _resolveComponent, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, child as _child, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue'; +"import { resolveComponent as _resolveComponent, setInsertionState as _setInsertionState, createComponentWithFallback as _createComponentWithFallback, child as _child, setProp as _setProp, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue'; const t0 = _template("
{{ bar }}
") const t1 = _template("
") @@ -161,8 +161,8 @@ export function render(_ctx, $props, $emit, $attrs, $slots) { const n1 = _createComponentWithFallback(_component_Comp) const n2 = _child(n3) _renderEffect(() => { - _setText(n2, _toDisplayString(_ctx.bar)) _setProp(n3, "id", _ctx.foo) + _setText(n2, _toDisplayString(_ctx.bar)) }) return [n0, n3] }" diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap index 5ae8a94f5b1..4a8caa65948 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformChildren.spec.ts.snap @@ -67,7 +67,6 @@ export function render(_ctx) { const x2 = _child(n2) _renderEffect(() => { const _msg = _ctx.msg - _setText(x0, _toDisplayString(_msg)) _setText(x1, _toDisplayString(_msg)) _setText(x2, _toDisplayString(_msg)) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap index 6e7d4229df8..46aac758377 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap @@ -55,7 +55,6 @@ export function render(_ctx) { const _foo = _ctx.foo const _bar = _ctx.bar const _foo_bar_baz = _foo[_bar(_ctx.baz)] - _setProp(n0, "id", _foo_bar_baz) _setProp(n1, "id", _foo_bar_baz) _setProp(n2, "id", _bar() + _foo) @@ -96,7 +95,6 @@ export function render(_ctx) { _renderEffect(() => { const _obj = _ctx.obj const _obj_foo_baz_obj_bar = _obj['foo']['baz'] + _obj.bar - _setProp(n0, "id", _obj_foo_baz_obj_bar) _setProp(n1, "id", _obj_foo_baz_obj_bar) }) @@ -115,7 +113,6 @@ export function render(_ctx) { _renderEffect(() => { const _foo = _ctx.foo const _foo_bar = _foo + _ctx.bar - _setProp(n0, "id", _foo_bar) _setProp(n1, "id", _foo_bar) _setProp(n2, "id", _foo + _foo_bar) @@ -133,7 +130,6 @@ export function render(_ctx) { const n1 = t0() _renderEffect(() => { const _foo_bar = _ctx.foo + _ctx.bar - _setProp(n0, "id", _foo_bar) _setProp(n1, "id", _foo_bar) }) @@ -166,7 +162,6 @@ export function render(_ctx) { const n1 = t0() _renderEffect(() => { const _foo = _ctx.foo - _setClass(n0, _foo) _setClass(n1, _foo) }) @@ -487,15 +482,13 @@ export function render(_ctx) { _setAttr(n0, "form", _ctx.form) _setAttr(n1, "list", _ctx.list) _setAttr(n2, "type", _ctx.type) - _setAttr(n3, "width", _width) - _setAttr(n4, "width", _width) - _setAttr(n5, "width", _width) - _setAttr(n6, "width", _width) - _setAttr(n3, "height", _height) + _setAttr(n4, "width", _width) _setAttr(n4, "height", _height) + _setAttr(n5, "width", _width) _setAttr(n5, "height", _height) + _setAttr(n6, "width", _width) _setAttr(n6, "height", _height) }) return [n0, n1, n2, n3, n4, n5, n6] diff --git a/packages/compiler-vapor/src/generators/operation.ts b/packages/compiler-vapor/src/generators/operation.ts index 4247bc6feca..081c4482992 100644 --- a/packages/compiler-vapor/src/generators/operation.ts +++ b/packages/compiler-vapor/src/generators/operation.ts @@ -99,10 +99,8 @@ export function genEffects( effects: IREffect[], context: CodegenContext, ): CodeFragment[] { - const { - helper, - block: { expressions }, - } = context + const { helper } = context + const expressions = effects.flatMap(effect => effect.expressions) const [frag, push, unshift] = buildCodeFragment() let operationsCount = 0 const { ids, frag: declarationFrags } = processExpressions( diff --git a/packages/compiler-vapor/src/ir/index.ts b/packages/compiler-vapor/src/ir/index.ts index da636113224..18f0139ab56 100644 --- a/packages/compiler-vapor/src/ir/index.ts +++ b/packages/compiler-vapor/src/ir/index.ts @@ -52,7 +52,6 @@ export interface BlockIRNode extends BaseIRNode { tempId: number effect: IREffect[] operation: OperationNode[] - expressions: SimpleExpressionNode[] returns: number[] } diff --git a/packages/compiler-vapor/src/transform.ts b/packages/compiler-vapor/src/transform.ts index 76563899d2b..964fefb1c38 100644 --- a/packages/compiler-vapor/src/transform.ts +++ b/packages/compiler-vapor/src/transform.ts @@ -137,8 +137,10 @@ export class TransformContext { registerEffect( expressions: SimpleExpressionNode[], - ...operations: OperationNode[] + operation: OperationNode | OperationNode[], + index: number = this.block.effect.length, ): void { + const operations = [operation].flat() expressions = expressions.filter(exp => !isConstantExpression(exp)) if ( this.inVOnce || @@ -150,26 +152,10 @@ export class TransformContext { return this.registerOperation(...operations) } - this.block.expressions.push(...expressions) - const existing = this.block.effect.find(e => - isSameExpression(e.expressions, expressions), - ) - if (existing) { - existing.operations.push(...operations) - } else { - this.block.effect.push({ - expressions, - operations, - }) - } - - function isSameExpression( - a: SimpleExpressionNode[], - b: SimpleExpressionNode[], - ) { - if (a.length !== b.length) return false - return a.every((exp, i) => exp.content === b[i].content) - } + this.block.effect.splice(index, 0, { + expressions, + operations, + }) } registerOperation(...node: OperationNode[]): void { diff --git a/packages/compiler-vapor/src/transforms/transformElement.ts b/packages/compiler-vapor/src/transforms/transformElement.ts index dceb3fd6121..44a1bbb65c3 100644 --- a/packages/compiler-vapor/src/transforms/transformElement.ts +++ b/packages/compiler-vapor/src/transforms/transformElement.ts @@ -44,6 +44,8 @@ export const isReservedProp: (key: string) => boolean = /*#__PURE__*/ makeMap( ) export const transformElement: NodeTransform = (node, context) => { + let effectIndex = context.block.effect.length + const getEffectIndex = () => effectIndex++ return function postTransformElement() { ;({ node } = context) if ( @@ -62,6 +64,7 @@ export const transformElement: NodeTransform = (node, context) => { context as TransformContext, isComponent, isDynamicComponent, + getEffectIndex, ) let { parent } = context @@ -78,13 +81,23 @@ export const transformElement: NodeTransform = (node, context) => { parent.node.children.filter(child => child.type !== NodeTypes.COMMENT) .length === 1 - ;(isComponent ? transformComponentElement : transformNativeElement)( - node as any, - propsResult, - singleRoot, - context as TransformContext, - isDynamicComponent, - ) + if (node.tagType === ElementTypes.COMPONENT) { + transformComponentElement( + node, + propsResult, + singleRoot, + context, + isDynamicComponent, + ) + } else { + transformNativeElement( + node, + propsResult, + singleRoot, + context, + getEffectIndex, + ) + } } } @@ -176,7 +189,8 @@ function transformNativeElement( node: PlainElementNode, propsResult: PropsResult, singleRoot: boolean, - context: TransformContext, + context: TransformContext, + getEffectIndex: () => number, ) { const { tag } = node const { scopeId } = context.options @@ -189,12 +203,16 @@ function transformNativeElement( const dynamicProps: string[] = [] if (propsResult[0] /* dynamic props */) { const [, dynamicArgs, expressions] = propsResult - context.registerEffect(expressions, { - type: IRNodeTypes.SET_DYNAMIC_PROPS, - element: context.reference(), - props: dynamicArgs, - root: singleRoot, - }) + context.registerEffect( + expressions, + { + type: IRNodeTypes.SET_DYNAMIC_PROPS, + element: context.reference(), + props: dynamicArgs, + root: singleRoot, + }, + getEffectIndex(), + ) } else { for (const prop of propsResult[1]) { const { key, values } = prop @@ -203,13 +221,17 @@ function transformNativeElement( if (values[0].content) template += `="${values[0].content}"` } else { dynamicProps.push(key.content) - context.registerEffect(values, { - type: IRNodeTypes.SET_PROP, - element: context.reference(), - prop, - root: singleRoot, - tag, - }) + context.registerEffect( + values, + { + type: IRNodeTypes.SET_PROP, + element: context.reference(), + prop, + root: singleRoot, + tag, + }, + getEffectIndex(), + ) } } } @@ -246,6 +268,7 @@ export function buildProps( context: TransformContext, isComponent: boolean, isDynamicComponent?: boolean, + getEffectIndex?: () => number, ): PropsResult { const props = node.props as (VaporDirectiveNode | AttributeNode)[] if (props.length === 0) return [false, []] @@ -292,12 +315,12 @@ export function buildProps( } else { context.registerEffect( [prop.exp], - { type: IRNodeTypes.SET_DYNAMIC_EVENTS, element: context.reference(), event: prop.exp, }, + getEffectIndex && getEffectIndex(), ) } } else { diff --git a/packages/compiler-vapor/src/transforms/utils.ts b/packages/compiler-vapor/src/transforms/utils.ts index b8e7adc6059..f7d0594fe58 100644 --- a/packages/compiler-vapor/src/transforms/utils.ts +++ b/packages/compiler-vapor/src/transforms/utils.ts @@ -29,7 +29,6 @@ export const newBlock = (node: BlockIRNode['node']): BlockIRNode => ({ effect: [], operation: [], returns: [], - expressions: [], tempId: 0, }) From 35bd90e526cec8bbb353b15475da4f7e2694f5fa Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Fri, 23 May 2025 08:50:31 +0800 Subject: [PATCH 2/6] Update packages/compiler-vapor/src/transforms/transformElement.ts Co-authored-by: edison --- packages/compiler-vapor/src/transforms/transformElement.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/compiler-vapor/src/transforms/transformElement.ts b/packages/compiler-vapor/src/transforms/transformElement.ts index 44a1bbb65c3..752f47625e0 100644 --- a/packages/compiler-vapor/src/transforms/transformElement.ts +++ b/packages/compiler-vapor/src/transforms/transformElement.ts @@ -81,7 +81,7 @@ export const transformElement: NodeTransform = (node, context) => { parent.node.children.filter(child => child.type !== NodeTypes.COMMENT) .length === 1 - if (node.tagType === ElementTypes.COMPONENT) { + if (isComponent) { transformComponentElement( node, propsResult, From 3af992a30d0681d399aefeff2c916cda6356dc1f Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Fri, 23 May 2025 08:49:12 +0800 Subject: [PATCH 3/6] chore: use isComponent --- packages/compiler-vapor/src/transforms/transformElement.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/compiler-vapor/src/transforms/transformElement.ts b/packages/compiler-vapor/src/transforms/transformElement.ts index 752f47625e0..642119a32b6 100644 --- a/packages/compiler-vapor/src/transforms/transformElement.ts +++ b/packages/compiler-vapor/src/transforms/transformElement.ts @@ -83,7 +83,7 @@ export const transformElement: NodeTransform = (node, context) => { if (isComponent) { transformComponentElement( - node, + node as ComponentNode, propsResult, singleRoot, context, @@ -91,7 +91,7 @@ export const transformElement: NodeTransform = (node, context) => { ) } else { transformNativeElement( - node, + node as PlainElementNode, propsResult, singleRoot, context, From ba9396156e3a463ccbf5bf7c42fcdff951ec88b5 Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Fri, 23 May 2025 09:33:55 +0800 Subject: [PATCH 4/6] fix: use getIndex --- packages/compiler-vapor/src/transform.ts | 4 ++-- packages/compiler-vapor/src/transforms/transformElement.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/compiler-vapor/src/transform.ts b/packages/compiler-vapor/src/transform.ts index 964fefb1c38..8eddaad4b01 100644 --- a/packages/compiler-vapor/src/transform.ts +++ b/packages/compiler-vapor/src/transform.ts @@ -138,7 +138,7 @@ export class TransformContext { registerEffect( expressions: SimpleExpressionNode[], operation: OperationNode | OperationNode[], - index: number = this.block.effect.length, + getIndex: () => number = () => this.block.effect.length, ): void { const operations = [operation].flat() expressions = expressions.filter(exp => !isConstantExpression(exp)) @@ -152,7 +152,7 @@ export class TransformContext { return this.registerOperation(...operations) } - this.block.effect.splice(index, 0, { + this.block.effect.splice(getIndex(), 0, { expressions, operations, }) diff --git a/packages/compiler-vapor/src/transforms/transformElement.ts b/packages/compiler-vapor/src/transforms/transformElement.ts index 642119a32b6..06e50004e3a 100644 --- a/packages/compiler-vapor/src/transforms/transformElement.ts +++ b/packages/compiler-vapor/src/transforms/transformElement.ts @@ -211,7 +211,7 @@ function transformNativeElement( props: dynamicArgs, root: singleRoot, }, - getEffectIndex(), + getEffectIndex, ) } else { for (const prop of propsResult[1]) { @@ -230,7 +230,7 @@ function transformNativeElement( root: singleRoot, tag, }, - getEffectIndex(), + getEffectIndex, ) } } @@ -320,7 +320,7 @@ export function buildProps( element: context.reference(), event: prop.exp, }, - getEffectIndex && getEffectIndex(), + getEffectIndex, ) } } else { From cb9631f53a0816a85ee9e02d4331803f248bf743 Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Fri, 23 May 2025 13:56:19 +0800 Subject: [PATCH 5/6] chore: add test --- .../__snapshots__/compile.spec.ts.snap | 39 ++++++++++++++++++- .../compiler-vapor/__tests__/compile.spec.ts | 17 ++++++++ packages/compiler-vapor/src/compile.ts | 2 +- packages/compiler-vapor/src/transform.ts | 2 +- 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap index 64dd32b6e75..b37348555c2 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap @@ -180,7 +180,7 @@ export function render(_ctx) { `; exports[`compile > dynamic root nodes and interpolation 1`] = ` -"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, delegateEvents as _delegateEvents, template as _template } from 'vue'; +"import { child as _child, setProp as _setProp, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, delegateEvents as _delegateEvents, template as _template } from 'vue'; const t0 = _template("", true) _delegateEvents("click") @@ -190,13 +190,48 @@ export function render(_ctx) { n0.$evtclick = e => _ctx.handleClick(e) _renderEffect(() => { const _count = _ctx.count - _setText(x0, _toDisplayString(_count) + "foo" + _toDisplayString(_count) + "foo" + _toDisplayString(_count)) _setProp(n0, "id", _count) + _setText(x0, _toDisplayString(_count) + "foo" + _toDisplayString(_count) + "foo" + _toDisplayString(_count)) + }) + return n0 +}" +`; + +exports[`compile > execution order > basic 1`] = ` +"import { child as _child, setProp as _setProp, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue'; +const t0 = _template("
", true) + +export function render(_ctx) { + const n0 = t0() + const x0 = _child(n0) + _renderEffect(() => { + _setProp(n0, "id", _ctx.foo) + _setText(x0, _toDisplayString(_ctx.bar)) }) return n0 }" `; +exports[`compile > execution order > with v-once 1`] = ` +"import { child as _child, next as _next, nthChild as _nthChild, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue'; +const t0 = _template("

", true) + +export function render(_ctx) { + const n3 = t0() + const n0 = _child(n3) + const n1 = _next(n0) + const n2 = _nthChild(n3, 3) + const x0 = _child(n0) + _setText(x0, _toDisplayString(_ctx.foo)) + _renderEffect(() => { + _setProp(n3, "id", _ctx.foo) + _setText(n1, " " + _toDisplayString(_ctx.bar)) + _setText(n2, " " + _toDisplayString(_ctx.baz)) + }) + return n3 +}" +`; + exports[`compile > expression parsing > interpolation 1`] = ` " const n0 = t0() diff --git a/packages/compiler-vapor/__tests__/compile.spec.ts b/packages/compiler-vapor/__tests__/compile.spec.ts index 33f399caa77..d11ec954637 100644 --- a/packages/compiler-vapor/__tests__/compile.spec.ts +++ b/packages/compiler-vapor/__tests__/compile.spec.ts @@ -220,4 +220,21 @@ describe('compile', () => { expect(code).matchSnapshot() }) }) + + describe('execution order', () => { + test('basic', () => { + const code = compile(`
{{ bar }}
`) + expect(code).matchSnapshot() + }) + test('with v-once', () => { + const code = compile( + `
+ {{ foo }} + {{ bar }}
+ {{ baz }} +
`, + ) + expect(code).matchSnapshot() + }) + }) }) diff --git a/packages/compiler-vapor/src/compile.ts b/packages/compiler-vapor/src/compile.ts index f84eafcbe0b..c39037a47d8 100644 --- a/packages/compiler-vapor/src/compile.ts +++ b/packages/compiler-vapor/src/compile.ts @@ -81,8 +81,8 @@ export function getBaseTransformPreset(): TransformPreset { transformVFor, transformSlotOutlet, transformTemplateRef, - transformText, transformElement, + transformText, transformVSlot, transformComment, transformChildren, diff --git a/packages/compiler-vapor/src/transform.ts b/packages/compiler-vapor/src/transform.ts index 8eddaad4b01..b3aba4a721b 100644 --- a/packages/compiler-vapor/src/transform.ts +++ b/packages/compiler-vapor/src/transform.ts @@ -138,7 +138,7 @@ export class TransformContext { registerEffect( expressions: SimpleExpressionNode[], operation: OperationNode | OperationNode[], - getIndex: () => number = () => this.block.effect.length, + getIndex = (): number => this.block.effect.length, ): void { const operations = [operation].flat() expressions = expressions.filter(exp => !isConstantExpression(exp)) From 2800da45aa9879019e4d2b9ad8fa7791cba77a60 Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Sat, 24 May 2025 08:10:39 +0800 Subject: [PATCH 6/6] chore: update test --- .../__tests__/__snapshots__/compile.spec.ts.snap | 3 +-- packages/compiler-vapor/__tests__/compile.spec.ts | 10 +++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap index b37348555c2..9359315c281 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap @@ -213,7 +213,7 @@ export function render(_ctx) { `; exports[`compile > execution order > with v-once 1`] = ` -"import { child as _child, next as _next, nthChild as _nthChild, toDisplayString as _toDisplayString, setText as _setText, setProp as _setProp, renderEffect as _renderEffect, template as _template } from 'vue'; +"import { child as _child, next as _next, nthChild as _nthChild, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue'; const t0 = _template("

", true) export function render(_ctx) { @@ -224,7 +224,6 @@ export function render(_ctx) { const x0 = _child(n0) _setText(x0, _toDisplayString(_ctx.foo)) _renderEffect(() => { - _setProp(n3, "id", _ctx.foo) _setText(n1, " " + _toDisplayString(_ctx.bar)) _setText(n2, " " + _toDisplayString(_ctx.baz)) }) diff --git a/packages/compiler-vapor/__tests__/compile.spec.ts b/packages/compiler-vapor/__tests__/compile.spec.ts index d11ec954637..95999789c74 100644 --- a/packages/compiler-vapor/__tests__/compile.spec.ts +++ b/packages/compiler-vapor/__tests__/compile.spec.ts @@ -225,16 +225,24 @@ describe('compile', () => { test('basic', () => { const code = compile(`
{{ bar }}
`) expect(code).matchSnapshot() + expect(code).contains( + `_setProp(n0, "id", _ctx.foo) + _setText(x0, _toDisplayString(_ctx.bar))`, + ) }) test('with v-once', () => { const code = compile( - `
+ `
{{ foo }} {{ bar }}
{{ baz }}
`, ) expect(code).matchSnapshot() + expect(code).contains( + `_setText(n1, " " + _toDisplayString(_ctx.bar)) + _setText(n2, " " + _toDisplayString(_ctx.baz))`, + ) }) }) })