Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement setRef update #191

Merged
merged 10 commits into from
Apr 30, 2024
Original file line number Diff line number Diff line change
@@ -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("<div></div>")

export function render(_ctx) {
const n0 = t0()
_setRef(n0, _ctx.foo)
let r0
_renderEffect(() => r0 = _setRef(n0, _ctx.foo, r0))
return n0
}"
`;
Expand All @@ -18,7 +19,7 @@ const t0 = _template("<div></div>")
export function render(_ctx) {
const n0 = _createFor(() => ([1,2,3]), (_block) => {
const n2 = t0()
_setRef(n2, "foo", true)
_setRef(n2, "foo", void 0, true)
return [n2, () => {}]
})
return n0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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", void 0, true)')
})
})
8 changes: 8 additions & 0 deletions packages/compiler-vapor/src/generators/ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}` : oper.refFor ? 'void 0' : undefined,
oper.refFor && 'true',
),
!!dynamicExp && ')',
]
}
1 change: 1 addition & 0 deletions packages/compiler-vapor/src/ir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ export interface SetRefIRNode extends BaseIRNode {
element: number
value: SimpleExpressionNode
refFor: boolean
refCount: number
}

export interface SetModelValueIRNode extends BaseIRNode {
Expand Down
3 changes: 3 additions & 0 deletions packages/compiler-vapor/src/transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ export interface TransformContext<T extends AllNode = AllNode> {

component: Set<string>

refCount: number

enterBlock(ir: TransformContext['block'], isVFor?: boolean): () => void
reference(): number
increaseId(): number
Expand Down Expand Up @@ -152,6 +154,7 @@ function createRootContext(
inVFor: 0,
comment: [],
component: root.component,
refCount: 0,

increaseId: () => globalId++,
reference() {
Expand Down
7 changes: 5 additions & 2 deletions packages/compiler-vapor/src/transforms/transformRef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand All @@ -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,
})
}
}
73 changes: 72 additions & 1 deletion packages/runtime-vapor/__tests__/dom/templateRef.spec.ts
Original file line number Diff line number Diff line change
@@ -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()
Expand All @@ -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('<div></div>')
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('<div></div>')
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)
})
})
20 changes: 19 additions & 1 deletion packages/runtime-vapor/src/dom/templateRef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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<string, any>) => {
callWithErrorHandling(
Expand Down Expand Up @@ -117,4 +134,5 @@ export function setRef(el: RefEl, ref: NodeRef, refFor = false) {
warn('Invalid template ref type:', ref, `(${typeof ref})`)
}
}
return ref
}
Loading