Skip to content

Commit

Permalink
wip: setup() template refs support
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed May 30, 2022
1 parent cae88de commit 4608565
Show file tree
Hide file tree
Showing 8 changed files with 591 additions and 30 deletions.
4 changes: 2 additions & 2 deletions src/core/instance/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ export function renderMixin(Vue: typeof Component) {

if (_parentVnode) {
vm.$scopedSlots = normalizeScopedSlots(
vm.$parent!,
_parentVnode.data!.scopedSlots,
vm.$slots,
vm.$scopedSlots
vm.$slots
)
if (vm._slotsProxy) {
syncSetupSlots(vm._slotsProxy, vm.$scopedSlots)
Expand Down
9 changes: 7 additions & 2 deletions src/core/vdom/create-functional-component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export function FunctionalRenderContext(
this.slots = () => {
if (!this.$slots) {
normalizeScopedSlots(
parent,
data.scopedSlots,
(this.$slots = resolveSlots(children, parent))
)
Expand All @@ -62,7 +63,7 @@ export function FunctionalRenderContext(
Object.defineProperty(this, 'scopedSlots', {
enumerable: true,
get() {
return normalizeScopedSlots(data.scopedSlots, this.slots())
return normalizeScopedSlots(parent, data.scopedSlots, this.slots())
}
} as any)

Expand All @@ -72,7 +73,11 @@ export function FunctionalRenderContext(
this.$options = options
// pre-resolve slots for renderSlot()
this.$slots = this.slots()
this.$scopedSlots = normalizeScopedSlots(data.scopedSlots, this.$slots)
this.$scopedSlots = normalizeScopedSlots(
parent,
data.scopedSlots,
this.$slots
)
}

if (options._scopeId) {
Expand Down
14 changes: 10 additions & 4 deletions src/core/vdom/helpers/normalize-scoped-slots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ import { normalizeChildren } from 'core/vdom/helpers/normalize-children'
import { emptyObject, isArray } from 'shared/util'
import { isAsyncPlaceholder } from './is-async-placeholder'
import type VNode from '../vnode'
import { Component } from 'typescript/component'
import { currentInstance, setCurrentInstance } from 'v3/currentInstance'

export function normalizeScopedSlots(
ownerVm: Component,
slots: { [key: string]: Function } | void,
normalSlots: { [key: string]: Array<VNode> },
prevSlots?: { [key: string]: Function } | void
normalSlots: { [key: string]: VNode[] }
): any {
let res
const prevSlots = ownerVm.$scopedSlots
const hasNormalSlots = Object.keys(normalSlots).length > 0
const isStable = slots ? !!slots.$stable : !hasNormalSlots
const key = slots && slots.$key
Expand All @@ -33,7 +36,7 @@ export function normalizeScopedSlots(
res = {}
for (const key in slots) {
if (slots[key] && key[0] !== '$') {
res[key] = normalizeScopedSlot(normalSlots, key, slots[key])
res[key] = normalizeScopedSlot(ownerVm, normalSlots, key, slots[key])
}
}
}
Expand All @@ -54,14 +57,17 @@ export function normalizeScopedSlots(
return res
}

function normalizeScopedSlot(normalSlots, key, fn) {
function normalizeScopedSlot(vm, normalSlots, key, fn) {
const normalized = function () {
const cur = currentInstance
setCurrentInstance(vm)
let res = arguments.length ? fn.apply(null, arguments) : fn({})
res =
res && typeof res === 'object' && !isArray(res)
? [res] // single vnode
: normalizeChildren(res)
const vnode: VNode | null = res && res[0]
setCurrentInstance(cur)
return res &&
(!vnode ||
(res.length === 1 && vnode.isComment && !isAsyncPlaceholder(vnode))) // #9658, #10391
Expand Down
84 changes: 66 additions & 18 deletions src/core/vdom/modules/template-ref.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import { remove, isDef, isArray } from 'shared/util'
import {
remove,
isDef,
hasOwn,
isArray,
isFunction,
invokeWithErrorHandling,
warn
} from 'core/util'
import type { VNodeWithData } from 'typescript/vnode'
import { Component } from 'typescript/component'
import { isRef } from 'v3'

export default {
create(_: any, vnode: VNodeWithData) {
Expand All @@ -17,28 +27,66 @@ export default {
}

export function registerRef(vnode: VNodeWithData, isRemoval?: boolean) {
const key = vnode.data.ref
if (!isDef(key)) return
const ref = vnode.data.ref
if (!isDef(ref)) return

const vm = vnode.context
const ref = vnode.componentInstance || vnode.elm
const refValue = vnode.componentInstance || vnode.elm
const value = isRemoval ? null : refValue

if (isFunction(ref)) {
invokeWithErrorHandling(ref, vm, [value], vm, `template ref function`)
return
}

const setupRefKey = vnode.data.ref_key
const isFor = vnode.data.refInFor
const _isString = typeof ref === 'string' || typeof ref === 'number'
const _isRef = isRef(ref)
const refs = vm.$refs
const obj = refs[key]
if (isRemoval) {
if (isArray(obj)) {
remove(obj, ref)
} else if (obj === ref) {
refs[key] = undefined
}
} else {
if (vnode.data.refInFor) {
if (!isArray(obj)) {
refs[key] = [ref]
} else if (obj.indexOf(ref) < 0) {
obj.push(ref)

if (_isString || _isRef) {
if (isFor) {
const existing = _isString ? refs[ref] : ref.value
if (isRemoval) {
isArray(existing) && remove(existing, refValue)
} else {
if (!isArray(existing)) {
if (_isString) {
refs[ref] = [refValue]
setSetupRef(vm, ref, refs[ref])
} else {
ref.value = [refValue]
if (setupRefKey) refs[setupRefKey] = ref.value as any
}
} else if (!existing.includes(refValue)) {
existing.push(refValue)
}
}
} else if (_isString) {
if (isRemoval && refs[ref] !== refValue) {
return
}
refs[ref] = value
setSetupRef(vm, ref, value)
} else if (_isRef) {
if (isRemoval && ref.value !== refValue) {
return
}
ref.value = value
if (setupRefKey) refs[setupRefKey] = value
} else if (__DEV__) {
warn(`Invalid template ref type: ${typeof ref}`)
}
}
}

function setSetupRef({ _setupState }: Component, key: string, val: any) {
if (_setupState && hasOwn(_setupState, key)) {
if (isRef(_setupState[key])) {
_setupState[key].value = val
} else {
refs[key] = ref
_setupState[key] = val
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ describe('ref', () => {
expect(vm.$refs.foo).toBe(vm.$el)
vm.value = 'bar'
waitForUpdate(() => {
expect(vm.$refs.foo).toBeUndefined()
expect(vm.$refs.foo).toBe(null)
expect(vm.$refs.bar).toBe(vm.$el)
}).then(done)
})
Expand Down Expand Up @@ -101,7 +101,7 @@ describe('ref', () => {
vm.test = ''
})
.then(() => {
expect(vm.$refs.test).toBeUndefined()
expect(vm.$refs.test).toBe(null)
})
.then(done)
})
Expand Down
2 changes: 1 addition & 1 deletion test/unit/features/v3/apiSetup.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ describe('api: setup context', () => {
const id = ref('foo')

const Child = {
setup(props: any, { slots }: any) {
setup(_props: any, { slots }: any) {
return () => h('div', [...slots.foo(), ...slots.bar()])
}
}
Expand Down
Loading

0 comments on commit 4608565

Please sign in to comment.