Skip to content

Commit 4608565

Browse files
committed
wip: setup() template refs support
1 parent cae88de commit 4608565

File tree

8 files changed

+591
-30
lines changed

8 files changed

+591
-30
lines changed

src/core/instance/render.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,9 @@ export function renderMixin(Vue: typeof Component) {
100100

101101
if (_parentVnode) {
102102
vm.$scopedSlots = normalizeScopedSlots(
103+
vm.$parent!,
103104
_parentVnode.data!.scopedSlots,
104-
vm.$slots,
105-
vm.$scopedSlots
105+
vm.$slots
106106
)
107107
if (vm._slotsProxy) {
108108
syncSetupSlots(vm._slotsProxy, vm.$scopedSlots)

src/core/vdom/create-functional-component.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export function FunctionalRenderContext(
5252
this.slots = () => {
5353
if (!this.$slots) {
5454
normalizeScopedSlots(
55+
parent,
5556
data.scopedSlots,
5657
(this.$slots = resolveSlots(children, parent))
5758
)
@@ -62,7 +63,7 @@ export function FunctionalRenderContext(
6263
Object.defineProperty(this, 'scopedSlots', {
6364
enumerable: true,
6465
get() {
65-
return normalizeScopedSlots(data.scopedSlots, this.slots())
66+
return normalizeScopedSlots(parent, data.scopedSlots, this.slots())
6667
}
6768
} as any)
6869

@@ -72,7 +73,11 @@ export function FunctionalRenderContext(
7273
this.$options = options
7374
// pre-resolve slots for renderSlot()
7475
this.$slots = this.slots()
75-
this.$scopedSlots = normalizeScopedSlots(data.scopedSlots, this.$slots)
76+
this.$scopedSlots = normalizeScopedSlots(
77+
parent,
78+
data.scopedSlots,
79+
this.$slots
80+
)
7681
}
7782

7883
if (options._scopeId) {

src/core/vdom/helpers/normalize-scoped-slots.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@ import { normalizeChildren } from 'core/vdom/helpers/normalize-children'
33
import { emptyObject, isArray } from 'shared/util'
44
import { isAsyncPlaceholder } from './is-async-placeholder'
55
import type VNode from '../vnode'
6+
import { Component } from 'typescript/component'
7+
import { currentInstance, setCurrentInstance } from 'v3/currentInstance'
68

79
export function normalizeScopedSlots(
10+
ownerVm: Component,
811
slots: { [key: string]: Function } | void,
9-
normalSlots: { [key: string]: Array<VNode> },
10-
prevSlots?: { [key: string]: Function } | void
12+
normalSlots: { [key: string]: VNode[] }
1113
): any {
1214
let res
15+
const prevSlots = ownerVm.$scopedSlots
1316
const hasNormalSlots = Object.keys(normalSlots).length > 0
1417
const isStable = slots ? !!slots.$stable : !hasNormalSlots
1518
const key = slots && slots.$key
@@ -33,7 +36,7 @@ export function normalizeScopedSlots(
3336
res = {}
3437
for (const key in slots) {
3538
if (slots[key] && key[0] !== '$') {
36-
res[key] = normalizeScopedSlot(normalSlots, key, slots[key])
39+
res[key] = normalizeScopedSlot(ownerVm, normalSlots, key, slots[key])
3740
}
3841
}
3942
}
@@ -54,14 +57,17 @@ export function normalizeScopedSlots(
5457
return res
5558
}
5659

57-
function normalizeScopedSlot(normalSlots, key, fn) {
60+
function normalizeScopedSlot(vm, normalSlots, key, fn) {
5861
const normalized = function () {
62+
const cur = currentInstance
63+
setCurrentInstance(vm)
5964
let res = arguments.length ? fn.apply(null, arguments) : fn({})
6065
res =
6166
res && typeof res === 'object' && !isArray(res)
6267
? [res] // single vnode
6368
: normalizeChildren(res)
6469
const vnode: VNode | null = res && res[0]
70+
setCurrentInstance(cur)
6571
return res &&
6672
(!vnode ||
6773
(res.length === 1 && vnode.isComment && !isAsyncPlaceholder(vnode))) // #9658, #10391

src/core/vdom/modules/template-ref.ts

Lines changed: 66 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
1-
import { remove, isDef, isArray } from 'shared/util'
1+
import {
2+
remove,
3+
isDef,
4+
hasOwn,
5+
isArray,
6+
isFunction,
7+
invokeWithErrorHandling,
8+
warn
9+
} from 'core/util'
210
import type { VNodeWithData } from 'typescript/vnode'
11+
import { Component } from 'typescript/component'
12+
import { isRef } from 'v3'
313

414
export default {
515
create(_: any, vnode: VNodeWithData) {
@@ -17,28 +27,66 @@ export default {
1727
}
1828

1929
export function registerRef(vnode: VNodeWithData, isRemoval?: boolean) {
20-
const key = vnode.data.ref
21-
if (!isDef(key)) return
30+
const ref = vnode.data.ref
31+
if (!isDef(ref)) return
2232

2333
const vm = vnode.context
24-
const ref = vnode.componentInstance || vnode.elm
34+
const refValue = vnode.componentInstance || vnode.elm
35+
const value = isRemoval ? null : refValue
36+
37+
if (isFunction(ref)) {
38+
invokeWithErrorHandling(ref, vm, [value], vm, `template ref function`)
39+
return
40+
}
41+
42+
const setupRefKey = vnode.data.ref_key
43+
const isFor = vnode.data.refInFor
44+
const _isString = typeof ref === 'string' || typeof ref === 'number'
45+
const _isRef = isRef(ref)
2546
const refs = vm.$refs
26-
const obj = refs[key]
27-
if (isRemoval) {
28-
if (isArray(obj)) {
29-
remove(obj, ref)
30-
} else if (obj === ref) {
31-
refs[key] = undefined
32-
}
33-
} else {
34-
if (vnode.data.refInFor) {
35-
if (!isArray(obj)) {
36-
refs[key] = [ref]
37-
} else if (obj.indexOf(ref) < 0) {
38-
obj.push(ref)
47+
48+
if (_isString || _isRef) {
49+
if (isFor) {
50+
const existing = _isString ? refs[ref] : ref.value
51+
if (isRemoval) {
52+
isArray(existing) && remove(existing, refValue)
53+
} else {
54+
if (!isArray(existing)) {
55+
if (_isString) {
56+
refs[ref] = [refValue]
57+
setSetupRef(vm, ref, refs[ref])
58+
} else {
59+
ref.value = [refValue]
60+
if (setupRefKey) refs[setupRefKey] = ref.value as any
61+
}
62+
} else if (!existing.includes(refValue)) {
63+
existing.push(refValue)
64+
}
65+
}
66+
} else if (_isString) {
67+
if (isRemoval && refs[ref] !== refValue) {
68+
return
3969
}
70+
refs[ref] = value
71+
setSetupRef(vm, ref, value)
72+
} else if (_isRef) {
73+
if (isRemoval && ref.value !== refValue) {
74+
return
75+
}
76+
ref.value = value
77+
if (setupRefKey) refs[setupRefKey] = value
78+
} else if (__DEV__) {
79+
warn(`Invalid template ref type: ${typeof ref}`)
80+
}
81+
}
82+
}
83+
84+
function setSetupRef({ _setupState }: Component, key: string, val: any) {
85+
if (_setupState && hasOwn(_setupState, key)) {
86+
if (isRef(_setupState[key])) {
87+
_setupState[key].value = val
4088
} else {
41-
refs[key] = ref
89+
_setupState[key] = val
4290
}
4391
}
4492
}

test/unit/features/ref.spec.ts renamed to test/unit/features/template-ref.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ describe('ref', () => {
5858
expect(vm.$refs.foo).toBe(vm.$el)
5959
vm.value = 'bar'
6060
waitForUpdate(() => {
61-
expect(vm.$refs.foo).toBeUndefined()
61+
expect(vm.$refs.foo).toBe(null)
6262
expect(vm.$refs.bar).toBe(vm.$el)
6363
}).then(done)
6464
})
@@ -101,7 +101,7 @@ describe('ref', () => {
101101
vm.test = ''
102102
})
103103
.then(() => {
104-
expect(vm.$refs.test).toBeUndefined()
104+
expect(vm.$refs.test).toBe(null)
105105
})
106106
.then(done)
107107
})

test/unit/features/v3/apiSetup.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ describe('api: setup context', () => {
164164
const id = ref('foo')
165165

166166
const Child = {
167-
setup(props: any, { slots }: any) {
167+
setup(_props: any, { slots }: any) {
168168
return () => h('div', [...slots.foo(), ...slots.bar()])
169169
}
170170
}

0 commit comments

Comments
 (0)