From f04c9c342d398c11111c873143dc437f588578ee Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Fri, 18 Jul 2025 15:00:04 +0800 Subject: [PATCH 1/9] fix(compiler-vapor): selectors was not initialized in time when the initial value of createFor source was not empty (#13642) --- .../transformTemplateRef.spec.ts.snap | 2 +- .../__snapshots__/vFor.spec.ts.snap | 20 +++-- .../__snapshots__/vOnce.spec.ts.snap | 2 +- packages/compiler-vapor/src/generators/for.ts | 21 +++-- .../__tests__/apiCreateSelector.spec.ts | 82 ++++--------------- packages/runtime-vapor/src/apiCreateFor.ts | 12 ++- 6 files changed, 53 insertions(+), 86 deletions(-) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap index 2d64e1ffe..7184446fc 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformTemplateRef.spec.ts.snap @@ -43,7 +43,7 @@ export function render(_ctx) { const n2 = t0() _setTemplateRef(n2, "foo", void 0, true) return n2 - }, null, 4) + }, undefined, 4) return n0 }" `; 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 141d3e410..69c695a24 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vFor.spec.ts.snap @@ -93,7 +93,7 @@ export function render(_ctx) { const x4 = _child(n4) _renderEffect(() => _setText(x4, _toDisplayString(_for_item1.value+_for_item0.value))) return n4 - }, null, 1) + }, undefined, 1) return n5 }) return n0 @@ -150,6 +150,7 @@ exports[`compiler: v-for > selector pattern 1`] = ` const t0 = _template(" ", true) export function render(_ctx) { + let _selector0_0 const n0 = _createFor(() => (_ctx.rows), (_for_item0) => { const n2 = t0() const x2 = _child(n2) @@ -157,8 +158,9 @@ export function render(_ctx) { _setText(x2, _toDisplayString(_ctx.selected === _for_item0.value.id ? 'danger' : '')) }) return n2 - }, (row) => (row.id)) - const _selector0_0 = n0.useSelector(() => _ctx.selected) + }, (row) => (row.id), undefined, ({ createSelector }) => { + _selector0_0 = createSelector(() => _ctx.selected) + }) return n0 }" `; @@ -168,14 +170,16 @@ exports[`compiler: v-for > selector pattern 2`] = ` const t0 = _template("", true) export function render(_ctx) { + let _selector0_0 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) + }, (row) => (row.id), undefined, ({ createSelector }) => { + _selector0_0 = createSelector(() => _ctx.selected) + }) return n0 }" `; @@ -202,14 +206,16 @@ exports[`compiler: v-for > selector pattern 4`] = ` const t0 = _template("", true) export function render(_ctx) { + let _selector0_0 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) + }, (row) => (row.id), undefined, ({ createSelector }) => { + _selector0_0 = createSelector(() => _ctx.selected) + }) return n0 }" `; diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap index ab3ade45b..b6107d5a1 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap @@ -68,7 +68,7 @@ export function render(_ctx) { const n0 = _createFor(() => (_ctx.list), (_for_item0) => { const n2 = t0() return n2 - }, null, 4) + }, undefined, 4) return n0 }" `; diff --git a/packages/compiler-vapor/src/generators/for.ts b/packages/compiler-vapor/src/generators/for.ts index 40f002a85..026db797b 100644 --- a/packages/compiler-vapor/src/generators/for.ts +++ b/packages/compiler-vapor/src/generators/for.ts @@ -99,19 +99,27 @@ export function genFor( keyProp, idMap, ) - const patternFrag: CodeFragment[] = [] + const selectorDeclarations: CodeFragment[] = [] + const selectorSetup: CodeFragment[] = [] for (let i = 0; i < selectorPatterns.length; i++) { const { selector } = selectorPatterns[i] const selectorName = `_selector${id}_${i}` - patternFrag.push( + selectorDeclarations.push(`let ${selectorName}`, NEWLINE) + if (i === 0) { + selectorSetup.push(`({ createSelector }) => {`, INDENT_START) + } + selectorSetup.push( NEWLINE, - `const ${selectorName} = `, - ...genCall(`n${id}.useSelector`, [ + `${selectorName} = `, + ...genCall(`createSelector`, [ `() => `, ...genExpression(selector, context), ]), ) + if (i === selectorPatterns.length - 1) { + selectorSetup.push(INDENT_END, NEWLINE, '}') + } } const blockFn = context.withId(() => { @@ -165,16 +173,17 @@ export function genFor( return [ NEWLINE, + ...selectorDeclarations, `const n${id} = `, ...genCall( - helper('createFor'), + [helper('createFor'), 'undefined'], sourceExpr, blockFn, genCallback(keyProp), flags ? String(flags) : undefined, + selectorSetup.length ? selectorSetup : undefined, // todo: hydrationNode ), - ...patternFrag, ] // construct a id -> accessor path map. diff --git a/packages/runtime-vapor/__tests__/apiCreateSelector.spec.ts b/packages/runtime-vapor/__tests__/apiCreateSelector.spec.ts index 9b36a2c31..9a3745fb5 100644 --- a/packages/runtime-vapor/__tests__/apiCreateSelector.spec.ts +++ b/packages/runtime-vapor/__tests__/apiCreateSelector.spec.ts @@ -1,12 +1,11 @@ import { ref } from '@vue/reactivity' import { makeRender } from './_utils' -// @ts-expect-error -import { createFor, createSelector, renderEffect } from '../src' +import { createFor } from '../src' import { nextTick } from '@vue/runtime-dom' const define = makeRender() -describe.todo('api: createSelector', () => { +describe('api: createSelector', () => { test('basic', async () => { let calledTimes = 0 let expectedCalledTimes = 0 @@ -15,19 +14,23 @@ describe.todo('api: createSelector', () => { const index = ref(0) const { host } = define(() => { - const isSleected = createSelector(index) + let selector: (cb: () => void) => void return createFor( () => list.value, item => { const span = document.createElement('li') - renderEffect(() => { + selector(() => { calledTimes += 1 const { id } = item.value - span.textContent = `${id}.${isSleected(id) ? 't' : 'f'}` + span.textContent = `${id}.${id === index.value ? 't' : 'f'}` }) return span }, item => item.id, + undefined, + ({ createSelector }) => { + selector = createSelector(() => index.value) + }, ) }).render() @@ -50,66 +53,11 @@ describe.todo('api: createSelector', () => { ) expect(calledTimes).toBe((expectedCalledTimes += 2)) - list.value[2].id = 3 - await nextTick() - expect(host.innerHTML).toBe( - '
  • 0.f
  • 1.f
  • 3.f
  • ', - ) - expect(calledTimes).toBe((expectedCalledTimes += 1)) - }) - - test('custom compare', async () => { - let calledTimes = 0 - let expectedCalledTimes = 0 - - const list = ref([{ id: 1 }, { id: 2 }, { id: 3 }]) - const index = ref(0) - - const { host } = define(() => { - const isSleected = createSelector( - index, - // @ts-expect-error - (key, value) => key === value + 1, - ) - return createFor( - () => list.value, - item => { - const span = document.createElement('li') - renderEffect(() => { - calledTimes += 1 - const { id } = item.value - span.textContent = `${id}.${isSleected(id) ? 't' : 'f'}` - }) - return span - }, - item => item.id, - ) - }).render() - - expect(host.innerHTML).toBe( - '
  • 1.t
  • 2.f
  • 3.f
  • ', - ) - expect(calledTimes).toBe((expectedCalledTimes += 3)) - - index.value = 1 - await nextTick() - expect(host.innerHTML).toBe( - '
  • 1.f
  • 2.t
  • 3.f
  • ', - ) - expect(calledTimes).toBe((expectedCalledTimes += 2)) - - index.value = 2 - await nextTick() - expect(host.innerHTML).toBe( - '
  • 1.f
  • 2.f
  • 3.t
  • ', - ) - expect(calledTimes).toBe((expectedCalledTimes += 2)) - - list.value[2].id = 4 - await nextTick() - expect(host.innerHTML).toBe( - '
  • 1.f
  • 2.f
  • 4.f
  • ', - ) - expect(calledTimes).toBe((expectedCalledTimes += 1)) + // list.value[2].id = 3 + // await nextTick() + // expect(host.innerHTML).toBe( + // '
  • 0.f
  • 1.f
  • 3.f
  • ', + // ) + // expect(calledTimes).toBe((expectedCalledTimes += 1)) }) }) diff --git a/packages/runtime-vapor/src/apiCreateFor.ts b/packages/runtime-vapor/src/apiCreateFor.ts index 426a5c56b..9ffdf6dca 100644 --- a/packages/runtime-vapor/src/apiCreateFor.ts +++ b/packages/runtime-vapor/src/apiCreateFor.ts @@ -74,6 +74,9 @@ export const createFor = ( ) => Block, getKey?: (item: any, key: any, index?: number) => any, flags = 0, + setup?: (_: { + createSelector: (source: () => any) => (cb: () => void) => void + }) => void, ): VaporFragment => { const _insertionParent = insertionParent const _insertionAnchor = insertionAnchor @@ -402,6 +405,10 @@ export const createFor = ( } } + if (setup) { + setup({ createSelector }) + } + if (flags & VaporVForFlags.ONCE) { renderList() } else { @@ -412,12 +419,9 @@ export const createFor = ( insert(frag, _insertionParent, _insertionAnchor) } - // @ts-expect-error - frag.useSelector = useSelector - return frag - function useSelector(source: () => any): (key: any, cb: () => void) => void { + function createSelector(source: () => any): (cb: () => void) => void { let operMap = new Map void)[]>() let activeKey = source() let activeOpers: (() => void)[] | undefined From a5e106d96eb17d73c8673e826393c910d5594a2f Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Fri, 18 Jul 2025 15:03:36 +0800 Subject: [PATCH 2/9] fix(compiler-vapor): handle special characters in cached variable names (#13626) --- .../transforms/__snapshots__/vOn.spec.ts.snap | 15 +++++++++++++++ .../__tests__/transforms/vOn.spec.ts | 12 ++++++++++++ .../compiler-vapor/src/generators/component.ts | 3 ++- .../compiler-vapor/src/generators/expression.ts | 2 +- 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOn.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOn.spec.ts.snap index dd00e5526..e7a2b30e6 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOn.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOn.spec.ts.snap @@ -12,6 +12,21 @@ export function render(_ctx) { }" `; +exports[`v-on > component event with special characters 1`] = ` +"import { resolveComponent as _resolveComponent, createComponentWithFallback as _createComponentWithFallback } from 'vue'; + +export function render(_ctx) { + const _component_Foo = _resolveComponent("Foo") + const _on_update_model = () => {} + const _on_update_model1 = () => {} + const n0 = _createComponentWithFallback(_component_Foo, { + "onUpdate:model": () => _on_update_model, + "onUpdate-model": () => _on_update_model1 + }, null, true) + return n0 +}" +`; + exports[`v-on > dynamic arg 1`] = ` "import { on as _on, renderEffect as _renderEffect, template as _template } from 'vue'; const t0 = _template("
    ", true) diff --git a/packages/compiler-vapor/__tests__/transforms/vOn.spec.ts b/packages/compiler-vapor/__tests__/transforms/vOn.spec.ts index aca88c791..963f46ad2 100644 --- a/packages/compiler-vapor/__tests__/transforms/vOn.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vOn.spec.ts @@ -695,4 +695,16 @@ describe('v-on', () => { expect(code).matchSnapshot() expect(code).include('n0.$evtclick = e => _ctx.handleClick(e)') }) + + test('component event with special characters', () => { + const { code } = compileWithVOn( + ``, + ) + + expect(code).matchSnapshot() + expect(code).contains('const _on_update_model = () => {}') + expect(code).contains('const _on_update_model1 = () => {}') + expect(code).contains('"onUpdate:model": () => _on_update_model') + expect(code).contains('"onUpdate-model": () => _on_update_model1') + }) }) diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts index 10705a2c7..fb76abaa5 100644 --- a/packages/compiler-vapor/src/generators/component.ts +++ b/packages/compiler-vapor/src/generators/component.ts @@ -26,7 +26,7 @@ import { genCall, genMulti, } from './utils' -import { genExpression } from './expression' +import { genExpression, genVarName } from './expression' import { genPropKey, genPropValue } from './prop' import { type SimpleExpressionNode, @@ -102,6 +102,7 @@ export function genCreateComponent( function getUniqueHandlerName(context: CodegenContext, name: string): string { const { seenInlineHandlerNames } = context + name = genVarName(name) const count = seenInlineHandlerNames[name] || 0 seenInlineHandlerNames[name] = count + 1 return count === 0 ? name : `${name}${count}` diff --git a/packages/compiler-vapor/src/generators/expression.ts b/packages/compiler-vapor/src/generators/expression.ts index aa7edf658..c70d4a566 100644 --- a/packages/compiler-vapor/src/generators/expression.ts +++ b/packages/compiler-vapor/src/generators/expression.ts @@ -647,7 +647,7 @@ function parseExp(context: CodegenContext, content: string): Node { return parseExpression(`(${content})`, options) } -function genVarName(exp: string): string { +export function genVarName(exp: string): string { return `${exp .replace(/[^a-zA-Z0-9]/g, '_') .replace(/_+/g, '_') From d1f2915cfe7915fa73624485ff3dd443176a31a9 Mon Sep 17 00:00:00 2001 From: Rizumu Ayaka Date: Fri, 18 Jul 2025 15:24:52 +0800 Subject: [PATCH 3/9] fix(compiler-vapor): handle empty interpolation (#13592) --- .../__snapshots__/expression.spec.ts.snap | 47 +++++++++++++++++++ .../__tests__/transforms/expression.spec.ts | 21 +++++++++ .../src/transforms/transformText.ts | 38 +++++++++++---- 3 files changed, 97 insertions(+), 9 deletions(-) diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap index 7e157236b..fda0121d6 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/expression.spec.ts.snap @@ -11,6 +11,53 @@ export function render(_ctx) { }" `; +exports[`compiler: expression > empty interpolation 1`] = ` +"import { template as _template } from 'vue'; +const t0 = _template(" ") + +export function render(_ctx) { + const n0 = t0() + return n0 +}" +`; + +exports[`compiler: expression > empty interpolation 2`] = ` +"import { template as _template } from 'vue'; +const t0 = _template(" ") + +export function render(_ctx) { + const n0 = t0() + return n0 +}" +`; + +exports[`compiler: expression > empty interpolation 3`] = ` +"import { template as _template } from 'vue'; +const t0 = _template("
    ", true) + +export function render(_ctx) { + const n0 = t0() + return n0 +}" +`; + +exports[`compiler: expression > empty interpolation 4`] = ` +"import { child as _child, toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue'; +const t0 = _template("
    ", true) + +export function render(_ctx) { + const n1 = t0() + const n0 = _child(n1) + const x1 = _child(n1) + _renderEffect(() => { + const _foo = _ctx.foo + _setText(n0, _toDisplayString(_foo)) + _setText(x1, _toDisplayString(_foo)) + }) + return n1 +}" +`; + exports[`compiler: expression > props 1`] = ` "import { toDisplayString as _toDisplayString, setText as _setText, renderEffect as _renderEffect, template as _template } from 'vue'; const t0 = _template(" ") diff --git a/packages/compiler-vapor/__tests__/transforms/expression.spec.ts b/packages/compiler-vapor/__tests__/transforms/expression.spec.ts index 5983bde67..9257a714b 100644 --- a/packages/compiler-vapor/__tests__/transforms/expression.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/expression.spec.ts @@ -47,4 +47,25 @@ describe('compiler: expression', () => { expect(code).toMatchSnapshot() expect(code).contains(`_String(_foo.id++)`) }) + + test('empty interpolation', () => { + const { code } = compileWithExpression(`{{}}`) + const { code: code2 } = compileWithExpression(`{{ }}`) + const { code: code3 } = compileWithExpression(`
    {{ }}
    `) + const { code: code4 } = compileWithExpression(`
    {{ foo }}{{ }}
    `) + + expect(code).toMatchSnapshot() + expect(code).not.toContain(`_toDisplayString`) + expect(code).not.toContain(`_setText`) + + expect(code2).toMatchSnapshot() + expect(code2).not.toContain(`_toDisplayString`) + expect(code2).not.toContain(`_setText`) + + expect(code3).toMatchSnapshot() + expect(code3).not.toContain(`_toDisplayString`) + expect(code3).not.toContain(`_setText`) + + expect(code4).toMatchSnapshot() + }) }) diff --git a/packages/compiler-vapor/src/transforms/transformText.ts b/packages/compiler-vapor/src/transforms/transformText.ts index 5f858058f..e9c273b85 100644 --- a/packages/compiler-vapor/src/transforms/transformText.ts +++ b/packages/compiler-vapor/src/transforms/transformText.ts @@ -87,7 +87,8 @@ export const transformText: NodeTransform = (node, context) => { } function processInterpolation(context: TransformContext) { - const children = context.parent!.node.children + const parentNode = context.parent!.node + const children = parentNode.children const nexts = children.slice(context.index) const idx = nexts.findIndex(n => !isTextLike(n)) const nodes = (idx > -1 ? nexts.slice(0, idx) : nexts) as Array @@ -97,10 +98,18 @@ function processInterpolation(context: TransformContext) { if (prev && prev.type === NodeTypes.TEXT) { nodes.unshift(prev) } + const values = processTextLikeChildren(nodes, context) + + if (values.length === 0 && parentNode.type !== NodeTypes.ROOT) { + return + } context.template += ' ' const id = context.reference() - const values = nodes.map(node => createTextLikeExpression(node, context)) + + if (values.length === 0) { + return + } const nonConstantExps = values.filter(v => !isConstantExpression(v)) const isStatic = @@ -129,8 +138,10 @@ function processTextContainer( children: TextLike[], context: TransformContext, ) { - const values = children.map(child => createTextLikeExpression(child, context)) + const values = processTextLikeChildren(children, context) + const literals = values.map(getLiteralExpressionValue) + if (literals.every(l => l != null)) { context.childrenTemplate = literals.map(l => String(l)) } else { @@ -149,13 +160,22 @@ function processTextContainer( } } -function createTextLikeExpression(node: TextLike, context: TransformContext) { - markNonTemplate(node, context) - if (node.type === NodeTypes.TEXT) { - return createSimpleExpression(node.content, true, node.loc) - } else { - return node.content as SimpleExpressionNode +function processTextLikeChildren(nodes: TextLike[], context: TransformContext) { + const exps: SimpleExpressionNode[] = [] + for (const node of nodes) { + let exp: SimpleExpressionNode + markNonTemplate(node, context) + + if (node.type === NodeTypes.TEXT) { + exp = createSimpleExpression(node.content, true, node.loc) + } else { + exp = node.content as SimpleExpressionNode + } + + if (exp.content) exps.push(exp) } + + return exps } function isTextLike(node: TemplateChildNode): node is TextLike { From ad21b1b7e96bc894f5df0d95fbd77c9ba6b15c2e Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Fri, 18 Jul 2025 15:26:05 +0800 Subject: [PATCH 4/9] fix(runtime-core): use __vapor instead of vapor to identify Vapor components (#13652) --- packages/runtime-core/src/hmr.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime-core/src/hmr.ts b/packages/runtime-core/src/hmr.ts index 6483e2241..eab407cb5 100644 --- a/packages/runtime-core/src/hmr.ts +++ b/packages/runtime-core/src/hmr.ts @@ -119,7 +119,7 @@ function reload(id: string, newComp: HMRComponent): void { // create a snapshot which avoids the set being mutated during updates const instances = [...record.instances] - if (newComp.vapor) { + if (newComp.__vapor) { for (const instance of instances) { instance.hmrReload!(newComp) } From 3cb27d156f6a30e8f950616a53a3726519eaf216 Mon Sep 17 00:00:00 2001 From: Doctor Wu Date: Fri, 18 Jul 2025 15:28:01 +0800 Subject: [PATCH 5/9] fix(reactivity): remove link check to align with 3.5 (#13654) close #13620 --- packages/reactivity/src/system.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/reactivity/src/system.ts b/packages/reactivity/src/system.ts index cc3eaad45..93adad335 100644 --- a/packages/reactivity/src/system.ts +++ b/packages/reactivity/src/system.ts @@ -77,14 +77,8 @@ export function link(dep: ReactiveNode, sub: ReactiveNode): void { return } } + // TODO: maybe can find a good way to check duplicate link const prevSub = dep.subsTail - if ( - prevSub !== undefined && - prevSub.sub === sub && - (!recursedCheck || isValidLink(prevSub, sub)) - ) { - return - } const newLink = (sub.depsTail = dep.subsTail = From b9fb79a1fd099b67e01c5fe5941551c0da3a0cae Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Fri, 18 Jul 2025 17:01:58 +0800 Subject: [PATCH 6/9] fix(reactivity): allow collect effects in EffectScope (#13657) close #13656 --- packages/reactivity/src/effectScope.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/packages/reactivity/src/effectScope.ts b/packages/reactivity/src/effectScope.ts index 9d8dd546e..36c9b85e8 100644 --- a/packages/reactivity/src/effectScope.ts +++ b/packages/reactivity/src/effectScope.ts @@ -1,11 +1,5 @@ import { EffectFlags, cleanup } from './effect' -import { - type Link, - type ReactiveNode, - link, - setActiveSub, - unlink, -} from './system' +import { type Link, type ReactiveNode, link, unlink } from './system' import { warn } from './warning' export let activeEffectScope: EffectScope | undefined @@ -65,14 +59,12 @@ export class EffectScope implements ReactiveNode { } run(fn: () => T): T | undefined { - const prevSub = setActiveSub() const prevScope = activeEffectScope try { activeEffectScope = this return fn() } finally { activeEffectScope = prevScope - setActiveSub(prevSub) } } From fca74b00a86c6039aa05591618539a77aaa72daf Mon Sep 17 00:00:00 2001 From: zhiyuanzmj <260480378@qq.com> Date: Fri, 18 Jul 2025 17:15:20 +0800 Subject: [PATCH 7/9] fix(runtime-vapor): remove access globalProperties warning (#13609) --- .../__tests__/apiCreateVaporApp.spec.ts | 4 ++-- packages/runtime-vapor/src/apiCreateApp.ts | 12 ------------ 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/packages/runtime-vapor/__tests__/apiCreateVaporApp.spec.ts b/packages/runtime-vapor/__tests__/apiCreateVaporApp.spec.ts index c82d432f1..6b21e5e55 100644 --- a/packages/runtime-vapor/__tests__/apiCreateVaporApp.spec.ts +++ b/packages/runtime-vapor/__tests__/apiCreateVaporApp.spec.ts @@ -341,7 +341,7 @@ describe('api: createVaporApp', () => { }) }) - test('config.globalProperty', () => { + test.todo('config.globalProperty', () => { const { app } = define({ setup() { return [] @@ -351,7 +351,7 @@ describe('api: createVaporApp', () => { app.config.globalProperties.msg = 'hello world' } catch (e) {} expect( - `app.config.globalProperties is not supported in vapor mode`, + `app.config.globalProperties is not supported in vapor mode components`, ).toHaveBeenWarned() }) }) diff --git a/packages/runtime-vapor/src/apiCreateApp.ts b/packages/runtime-vapor/src/apiCreateApp.ts index ee4c00c88..bcc680eae 100644 --- a/packages/runtime-vapor/src/apiCreateApp.ts +++ b/packages/runtime-vapor/src/apiCreateApp.ts @@ -88,18 +88,6 @@ function prepareApp() { } function postPrepareApp(app: App) { - if (__DEV__) { - app.config.globalProperties = new Proxy( - {}, - { - set() { - warn(`app.config.globalProperties is not supported in vapor mode.`) - return false - }, - }, - ) - } - app.vapor = true const mount = app.mount app.mount = (container, ...args: any[]) => { From 2be828a0c165c7f1533ace0bd81fba43a2af16d6 Mon Sep 17 00:00:00 2001 From: edison Date: Fri, 18 Jul 2025 17:34:50 +0800 Subject: [PATCH 8/9] fix(runtime-vapor): handle v-model vdom interop error (#13643) --- packages/runtime-core/src/renderer.ts | 2 +- .../__tests__/vdomInterop.spec.ts | 69 ++++++++++++++++++- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index bad40f143..30f9e6b2c 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -2727,7 +2727,7 @@ export function traverseStaticChildren( function locateNonHydratedAsyncRoot( instance: ComponentInternalInstance, ): ComponentInternalInstance | undefined { - const subComponent = instance.subTree.component + const subComponent = instance.vapor ? null : instance.subTree.component if (subComponent) { if (subComponent.asyncDep && !subComponent.asyncResolved) { return subComponent diff --git a/packages/runtime-vapor/__tests__/vdomInterop.spec.ts b/packages/runtime-vapor/__tests__/vdomInterop.spec.ts index 96222e6ee..98c4cdbc9 100644 --- a/packages/runtime-vapor/__tests__/vdomInterop.spec.ts +++ b/packages/runtime-vapor/__tests__/vdomInterop.spec.ts @@ -1,6 +1,23 @@ -import { createVNode, defineComponent, h, renderSlot } from '@vue/runtime-dom' +import { + createVNode, + defineComponent, + h, + nextTick, + ref, + renderSlot, + toDisplayString, + useModel, +} from '@vue/runtime-dom' import { makeInteropRender } from './_utils' -import { createComponent, defineVaporComponent } from '../src' +import { + applyTextModel, + child, + createComponent, + defineVaporComponent, + renderEffect, + setText, + template, +} from '../src' const define = makeInteropRender() @@ -26,6 +43,54 @@ describe('vdomInterop', () => { }) }) + describe('v-model', () => { + test('basic work', async () => { + const VaporChild = defineVaporComponent({ + props: { + modelValue: {}, + modelModifiers: {}, + }, + emits: ['update:modelValue'], + setup(__props) { + const modelValue = useModel(__props, 'modelValue') + + const n0 = template('

    ')() as any + const n1 = template('')() as any + const x0 = child(n0) as any + applyTextModel( + n1, + () => modelValue.value, + _value => (modelValue.value = _value), + ) + renderEffect(() => setText(x0, toDisplayString(modelValue.value))) + return [n0, n1] + }, + }) + + const { html, host } = define({ + setup() { + const msg = ref('foo') + return () => + h(VaporChild as any, { + modelValue: msg.value, + 'onUpdate:modelValue': (value: string) => { + msg.value = value + }, + }) + }, + }).render() + + expect(html()).toBe('

    foo

    ') + + const inputEl = host.querySelector('input')! + inputEl.value = 'bar' + inputEl.dispatchEvent(new Event('input')) + + await nextTick() + expect(html()).toBe('

    bar

    ') + }) + }) + describe('emit', () => { test('emit from vapor child to vdom parent', () => { const VaporChild = defineVaporComponent({ From 56a7f9dd181b9781ec2c684cad49fecd35d14178 Mon Sep 17 00:00:00 2001 From: daiwei Date: Fri, 18 Jul 2025 17:37:08 +0800 Subject: [PATCH 9/9] release: v3.6.0-alpha.2 --- CHANGELOG.md | 17 +++++++++++++++++ package.json | 2 +- packages/compiler-core/package.json | 2 +- packages/compiler-dom/package.json | 2 +- packages/compiler-sfc/package.json | 2 +- packages/compiler-ssr/package.json | 2 +- packages/compiler-vapor/package.json | 2 +- packages/reactivity/package.json | 2 +- packages/runtime-core/package.json | 2 +- packages/runtime-dom/package.json | 2 +- packages/runtime-vapor/package.json | 2 +- packages/server-renderer/package.json | 2 +- packages/shared/package.json | 2 +- packages/vue-compat/package.json | 2 +- packages/vue/package.json | 2 +- 15 files changed, 31 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53a880885..6e787f1a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +# [3.6.0-alpha.2](https://github.com/vuejs/core/compare/v3.6.0-alpha.1...v3.6.0-alpha.2) (2025-07-18) + + +### Bug Fixes + +* **compiler-vapor:** handle empty interpolation ([#13592](https://github.com/vuejs/core/issues/13592)) ([d1f2915](https://github.com/vuejs/core/commit/d1f2915cfe7915fa73624485ff3dd443176a31a9)) +* **compiler-vapor:** handle special characters in cached variable names ([#13626](https://github.com/vuejs/core/issues/13626)) ([a5e106d](https://github.com/vuejs/core/commit/a5e106d96eb17d73c8673e826393c910d5594a2f)) +* **compiler-vapor:** selectors was not initialized in time when the initial value of createFor source was not empty ([#13642](https://github.com/vuejs/core/issues/13642)) ([f04c9c3](https://github.com/vuejs/core/commit/f04c9c342d398c11111c873143dc437f588578ee)) +* **reactivity:** allow collect effects in EffectScope ([#13657](https://github.com/vuejs/core/issues/13657)) ([b9fb79a](https://github.com/vuejs/core/commit/b9fb79a1fd099b67e01c5fe5941551c0da3a0cae)), closes [#13656](https://github.com/vuejs/core/issues/13656) +* **reactivity:** remove link check to align with 3.5 ([#13654](https://github.com/vuejs/core/issues/13654)) ([3cb27d1](https://github.com/vuejs/core/commit/3cb27d156f6a30e8f950616a53a3726519eaf216)), closes [#13620](https://github.com/vuejs/core/issues/13620) +* **runtime-core:** use __vapor instead of vapor to identify Vapor components ([#13652](https://github.com/vuejs/core/issues/13652)) ([ad21b1b](https://github.com/vuejs/core/commit/ad21b1b7e96bc894f5df0d95fbd77c9ba6b15c2e)) +* **runtime-vapor:** component emits vdom interop ([#13498](https://github.com/vuejs/core/issues/13498)) ([d95fc18](https://github.com/vuejs/core/commit/d95fc186c26e81345cd75037c3c1304b0eae13b4)) +* **runtime-vapor:** handle v-model vdom interop error ([#13643](https://github.com/vuejs/core/issues/13643)) ([2be828a](https://github.com/vuejs/core/commit/2be828a0c165c7f1533ace0bd81fba43a2af16d6)) +* **runtime-vapor:** remove access globalProperties warning ([#13609](https://github.com/vuejs/core/issues/13609)) ([fca74b0](https://github.com/vuejs/core/commit/fca74b00a86c6039aa05591618539a77aaa72daf)) + + + # [3.6.0-alpha.1](https://github.com/vuejs/core/compare/v3.5.17...v3.6.0-alpha.1) (2025-07-12) ### Features diff --git a/package.json b/package.json index d7aa84148..4ecb490a4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "private": true, - "version": "3.6.0-alpha.1", + "version": "3.6.0-alpha.2", "packageManager": "pnpm@10.12.4", "type": "module", "scripts": { diff --git a/packages/compiler-core/package.json b/packages/compiler-core/package.json index a42fa8465..e76d747fd 100644 --- a/packages/compiler-core/package.json +++ b/packages/compiler-core/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-core", - "version": "3.6.0-alpha.1", + "version": "3.6.0-alpha.2", "description": "@vue/compiler-core", "main": "index.js", "module": "dist/compiler-core.esm-bundler.js", diff --git a/packages/compiler-dom/package.json b/packages/compiler-dom/package.json index e65d2b438..3224f5bdb 100644 --- a/packages/compiler-dom/package.json +++ b/packages/compiler-dom/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-dom", - "version": "3.6.0-alpha.1", + "version": "3.6.0-alpha.2", "description": "@vue/compiler-dom", "main": "index.js", "module": "dist/compiler-dom.esm-bundler.js", diff --git a/packages/compiler-sfc/package.json b/packages/compiler-sfc/package.json index c77cc27ee..77d4e38b6 100644 --- a/packages/compiler-sfc/package.json +++ b/packages/compiler-sfc/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-sfc", - "version": "3.6.0-alpha.1", + "version": "3.6.0-alpha.2", "description": "@vue/compiler-sfc", "main": "dist/compiler-sfc.cjs.js", "module": "dist/compiler-sfc.esm-browser.js", diff --git a/packages/compiler-ssr/package.json b/packages/compiler-ssr/package.json index fc0ffd2e3..38b08c4f6 100644 --- a/packages/compiler-ssr/package.json +++ b/packages/compiler-ssr/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-ssr", - "version": "3.6.0-alpha.1", + "version": "3.6.0-alpha.2", "description": "@vue/compiler-ssr", "main": "dist/compiler-ssr.cjs.js", "types": "dist/compiler-ssr.d.ts", diff --git a/packages/compiler-vapor/package.json b/packages/compiler-vapor/package.json index efb667961..81a37cde0 100644 --- a/packages/compiler-vapor/package.json +++ b/packages/compiler-vapor/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compiler-vapor", - "version": "3.6.0-alpha.1", + "version": "3.6.0-alpha.2", "description": "@vue/compiler-vapor", "main": "dist/compiler-vapor.cjs.js", "module": "dist/compiler-vapor.esm-bundler.js", diff --git a/packages/reactivity/package.json b/packages/reactivity/package.json index c07f80090..76c9a08b6 100644 --- a/packages/reactivity/package.json +++ b/packages/reactivity/package.json @@ -1,6 +1,6 @@ { "name": "@vue/reactivity", - "version": "3.6.0-alpha.1", + "version": "3.6.0-alpha.2", "description": "@vue/reactivity", "main": "index.js", "module": "dist/reactivity.esm-bundler.js", diff --git a/packages/runtime-core/package.json b/packages/runtime-core/package.json index 527869ae7..d379ef7f4 100644 --- a/packages/runtime-core/package.json +++ b/packages/runtime-core/package.json @@ -1,6 +1,6 @@ { "name": "@vue/runtime-core", - "version": "3.6.0-alpha.1", + "version": "3.6.0-alpha.2", "description": "@vue/runtime-core", "main": "index.js", "module": "dist/runtime-core.esm-bundler.js", diff --git a/packages/runtime-dom/package.json b/packages/runtime-dom/package.json index 934499370..36fb787fa 100644 --- a/packages/runtime-dom/package.json +++ b/packages/runtime-dom/package.json @@ -1,6 +1,6 @@ { "name": "@vue/runtime-dom", - "version": "3.6.0-alpha.1", + "version": "3.6.0-alpha.2", "description": "@vue/runtime-dom", "main": "index.js", "module": "dist/runtime-dom.esm-bundler.js", diff --git a/packages/runtime-vapor/package.json b/packages/runtime-vapor/package.json index 9a61a3cd3..f27ef46b7 100644 --- a/packages/runtime-vapor/package.json +++ b/packages/runtime-vapor/package.json @@ -1,6 +1,6 @@ { "name": "@vue/runtime-vapor", - "version": "3.6.0-alpha.1", + "version": "3.6.0-alpha.2", "description": "@vue/runtime-vapor", "main": "index.js", "module": "dist/runtime-vapor.esm-bundler.js", diff --git a/packages/server-renderer/package.json b/packages/server-renderer/package.json index 039414ca8..cb4da84e4 100644 --- a/packages/server-renderer/package.json +++ b/packages/server-renderer/package.json @@ -1,6 +1,6 @@ { "name": "@vue/server-renderer", - "version": "3.6.0-alpha.1", + "version": "3.6.0-alpha.2", "description": "@vue/server-renderer", "main": "index.js", "module": "dist/server-renderer.esm-bundler.js", diff --git a/packages/shared/package.json b/packages/shared/package.json index 13656d505..b4703addd 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -1,6 +1,6 @@ { "name": "@vue/shared", - "version": "3.6.0-alpha.1", + "version": "3.6.0-alpha.2", "description": "internal utils shared across @vue packages", "main": "index.js", "module": "dist/shared.esm-bundler.js", diff --git a/packages/vue-compat/package.json b/packages/vue-compat/package.json index e02f068ff..cd2917e6b 100644 --- a/packages/vue-compat/package.json +++ b/packages/vue-compat/package.json @@ -1,6 +1,6 @@ { "name": "@vue/compat", - "version": "3.6.0-alpha.1", + "version": "3.6.0-alpha.2", "description": "Vue 3 compatibility build for Vue 2", "main": "index.js", "module": "dist/vue.runtime.esm-bundler.js", diff --git a/packages/vue/package.json b/packages/vue/package.json index 6b3069db0..ab734ee7a 100644 --- a/packages/vue/package.json +++ b/packages/vue/package.json @@ -1,6 +1,6 @@ { "name": "vue", - "version": "3.6.0-alpha.1", + "version": "3.6.0-alpha.2", "description": "The progressive JavaScript framework for building modern web UI.", "main": "index.js", "module": "dist/vue.runtime.esm-bundler.js",