diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformRef.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformRef.spec.ts.snap
index ba867bd75..cc0147649 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformRef.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/transformRef.spec.ts.snap
@@ -1,12 +1,13 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`compiler: template ref transform > dynamic ref 1`] = `
-"import { setRef as _setRef, template as _template } from 'vue/vapor';
+"import { renderEffect as _renderEffect, setRef as _setRef, template as _template } from 'vue/vapor';
const t0 = _template("
")
export function render(_ctx) {
const n0 = t0()
- _setRef(n0, _ctx.foo)
+ let r0
+ _renderEffect(() => r0 = _setRef(n0, _ctx.foo, r0))
return n0
}"
`;
@@ -18,7 +19,7 @@ const t0 = _template("")
export function render(_ctx) {
const n0 = _createFor(() => ([1,2,3]), (_block) => {
const n2 = t0()
- _setRef(n2, "foo", true)
+ _setRef(n2, "foo", undefined, true)
return [n2, () => {}]
})
return n0
@@ -32,7 +33,7 @@ const t0 = _template("")
export function render(_ctx) {
const n0 = _createIf(() => (true), () => {
const n2 = t0()
- _setRef(n2, "foo")
+ _setRef(n2, "foo", undefined)
return n2
})
return n0
@@ -45,7 +46,7 @@ const t0 = _template("")
export function render(_ctx) {
const n0 = t0()
- _setRef(n0, "foo")
+ _setRef(n0, "foo", undefined)
return n0
}"
`;
diff --git a/packages/compiler-vapor/__tests__/transforms/transformRef.spec.ts b/packages/compiler-vapor/__tests__/transforms/transformRef.spec.ts
index 78b7694ad..83d19816f 100644
--- a/packages/compiler-vapor/__tests__/transforms/transformRef.spec.ts
+++ b/packages/compiler-vapor/__tests__/transforms/transformRef.spec.ts
@@ -45,7 +45,7 @@ describe('compiler: template ref transform', () => {
})
expect(code).matchSnapshot()
- expect(code).contains('_setRef(n0, "foo")')
+ expect(code).contains('_setRef(n0, "foo", undefined)')
})
test('dynamic ref', () => {
@@ -70,7 +70,7 @@ describe('compiler: template ref transform', () => {
},
})
expect(code).matchSnapshot()
- expect(code).contains('_setRef(n0, _ctx.foo)')
+ expect(code).contains('_setRef(n0, _ctx.foo, r0)')
})
test('ref + v-if', () => {
@@ -98,7 +98,7 @@ describe('compiler: template ref transform', () => {
})
expect(code).matchSnapshot()
- expect(code).contains('_setRef(n2, "foo")')
+ expect(code).contains('_setRef(n2, "foo", undefined)')
})
test('ref + v-for', () => {
@@ -122,6 +122,6 @@ describe('compiler: template ref transform', () => {
refFor: true,
})
expect(code).matchSnapshot()
- expect(code).contains('_setRef(n2, "foo", true)')
+ expect(code).contains('_setRef(n2, "foo", undefined, true)')
})
})
diff --git a/packages/compiler-vapor/src/generators/ref.ts b/packages/compiler-vapor/src/generators/ref.ts
index 248221844..753a861bf 100644
--- a/packages/compiler-vapor/src/generators/ref.ts
+++ b/packages/compiler-vapor/src/generators/ref.ts
@@ -8,13 +8,21 @@ export function genSetRef(
context: CodegenContext,
): CodeFragment[] {
const { vaporHelper } = context
+ const dynamicExp = oper.refCount !== -1
return [
NEWLINE,
+ dynamicExp && `let r${oper.refCount}`,
+ dynamicExp && NEWLINE,
+ ...(!!dynamicExp
+ ? [`${vaporHelper('renderEffect')}(() => `, `r${oper.refCount} = `]
+ : []),
...genCall(
vaporHelper('setRef'),
[`n${oper.element}`],
genExpression(oper.value, context),
+ [dynamicExp ? `r${oper.refCount}` : 'undefined'],
oper.refFor && 'true',
),
+ !!dynamicExp && ')',
]
}
diff --git a/packages/compiler-vapor/src/ir.ts b/packages/compiler-vapor/src/ir.ts
index 40ccf3bf1..5a68ce0fb 100644
--- a/packages/compiler-vapor/src/ir.ts
+++ b/packages/compiler-vapor/src/ir.ts
@@ -145,6 +145,7 @@ export interface SetRefIRNode extends BaseIRNode {
element: number
value: SimpleExpressionNode
refFor: boolean
+ refCount: number
}
export interface SetModelValueIRNode extends BaseIRNode {
diff --git a/packages/compiler-vapor/src/transform.ts b/packages/compiler-vapor/src/transform.ts
index bc2de550a..27aa62d2f 100644
--- a/packages/compiler-vapor/src/transform.ts
+++ b/packages/compiler-vapor/src/transform.ts
@@ -79,6 +79,8 @@ export interface TransformContext {
component: Set
+ refCount: number
+
enterBlock(ir: TransformContext['block'], isVFor?: boolean): () => void
reference(): number
increaseId(): number
@@ -152,6 +154,7 @@ function createRootContext(
inVFor: 0,
comment: [],
component: root.component,
+ refCount: 0,
increaseId: () => globalId++,
reference() {
diff --git a/packages/compiler-vapor/src/transforms/transformRef.ts b/packages/compiler-vapor/src/transforms/transformRef.ts
index 7da99ebfc..402999026 100644
--- a/packages/compiler-vapor/src/transforms/transformRef.ts
+++ b/packages/compiler-vapor/src/transforms/transformRef.ts
@@ -6,7 +6,7 @@ import {
import type { NodeTransform } from '../transform'
import { IRNodeTypes } from '../ir'
import { normalizeBindShorthand } from './vBind'
-import { findProp } from '../utils'
+import { findProp, isConstantExpression } from '../utils'
import { EMPTY_EXPRESSION } from './utils'
export const transformRef: NodeTransform = (node, context) => {
@@ -24,11 +24,14 @@ export const transformRef: NodeTransform = (node, context) => {
: EMPTY_EXPRESSION
}
- return () =>
+ return () => {
+ const dynamicExp = !isConstantExpression(value)
context.registerOperation({
type: IRNodeTypes.SET_REF,
element: context.reference(),
value,
refFor: !!context.inVFor,
+ refCount: dynamicExp ? context.refCount++ : -1,
})
+ }
}
diff --git a/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts b/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts
index 6dc10eff3..7ae406723 100644
--- a/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts
+++ b/packages/runtime-vapor/__tests__/dom/templateRef.spec.ts
@@ -1,4 +1,12 @@
-import { ref, setRef, template } from '../../src'
+import type { NodeRef } from 'packages/runtime-vapor/src/dom/templateRef'
+import {
+ createIf,
+ nextTick,
+ ref,
+ renderEffect,
+ setRef,
+ template,
+} from '../../src'
import { makeRender } from '../_utils'
const define = makeRender()
@@ -23,4 +31,67 @@ describe('api: template ref', () => {
const { host } = render()
expect(el.value).toBe(host.children[0])
})
+
+ it('string ref update', async () => {
+ const t0 = template('')
+ const fooEl = ref(null)
+ const barEl = ref(null)
+ const refKey = ref('foo')
+
+ const { render } = define({
+ setup() {
+ return {
+ foo: fooEl,
+ bar: barEl,
+ }
+ },
+ render() {
+ const n0 = t0()
+ let r0: NodeRef | undefined
+ renderEffect(() => {
+ r0 = setRef(n0 as Element, refKey.value, r0)
+ })
+ return n0
+ },
+ })
+ const { host } = render()
+ expect(fooEl.value).toBe(host.children[0])
+ expect(barEl.value).toBe(null)
+
+ refKey.value = 'bar'
+ await nextTick()
+ expect(barEl.value).toBe(host.children[0])
+ expect(fooEl.value).toBe(null)
+ })
+
+ it('string ref unmount', async () => {
+ const t0 = template('')
+ const el = ref(null)
+ const toggle = ref(true)
+
+ const { render } = define({
+ setup() {
+ return {
+ refKey: el,
+ }
+ },
+ render() {
+ const n0 = createIf(
+ () => toggle.value,
+ () => {
+ const n1 = t0()
+ setRef(n1 as Element, 'refKey')
+ return n1
+ },
+ )
+ return n0
+ },
+ })
+ const { host } = render()
+ expect(el.value).toBe(host.children[0])
+
+ toggle.value = false
+ await nextTick()
+ expect(el.value).toBe(null)
+ })
})
diff --git a/packages/runtime-vapor/src/dom/templateRef.ts b/packages/runtime-vapor/src/dom/templateRef.ts
index 25a9b014b..b3770739a 100644
--- a/packages/runtime-vapor/src/dom/templateRef.ts
+++ b/packages/runtime-vapor/src/dom/templateRef.ts
@@ -27,7 +27,12 @@ export type RefEl = Element | ComponentInternalInstance
/**
* Function for handling a template ref
*/
-export function setRef(el: RefEl, ref: NodeRef, refFor = false) {
+export function setRef(
+ el: RefEl,
+ ref: NodeRef,
+ oldRef?: NodeRef,
+ refFor = false,
+) {
if (!currentInstance) return
const { setupState, isUnmounted } = currentInstance
@@ -42,6 +47,18 @@ export function setRef(el: RefEl, ref: NodeRef, refFor = false) {
? (currentInstance.refs = {})
: currentInstance.refs
+ // dynamic ref changed. unset old ref
+ if (oldRef != null && oldRef !== ref) {
+ if (isString(oldRef)) {
+ refs[oldRef] = null
+ if (hasOwn(setupState, oldRef)) {
+ setupState[oldRef] = null
+ }
+ } else if (isRef(oldRef)) {
+ oldRef.value = null
+ }
+ }
+
if (isFunction(ref)) {
const invokeRefSetter = (value?: Element | Record) => {
callWithErrorHandling(
@@ -117,4 +134,5 @@ export function setRef(el: RefEl, ref: NodeRef, refFor = false) {
warn('Invalid template ref type:', ref, `(${typeof ref})`)
}
}
+ return ref
}