mirror of
https://gitee.com/vuejs/vue.git
synced 2024-12-02 03:57:36 +08:00
wip: setup() template refs support
This commit is contained in:
parent
cae88defa2
commit
460856510d
@ -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)
|
||||
|
@ -52,6 +52,7 @@ export function FunctionalRenderContext(
|
||||
this.slots = () => {
|
||||
if (!this.$slots) {
|
||||
normalizeScopedSlots(
|
||||
parent,
|
||||
data.scopedSlots,
|
||||
(this.$slots = resolveSlots(children, parent))
|
||||
)
|
||||
@ -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)
|
||||
|
||||
@ -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) {
|
||||
|
@ -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
|
||||
@ -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])
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
@ -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) {
|
||||
@ -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 {
|
||||
refs[key] = ref
|
||||
} 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 {
|
||||
_setupState[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
})
|
||||
@ -101,7 +101,7 @@ describe('ref', () => {
|
||||
vm.test = ''
|
||||
})
|
||||
.then(() => {
|
||||
expect(vm.$refs.test).toBeUndefined()
|
||||
expect(vm.$refs.test).toBe(null)
|
||||
})
|
||||
.then(done)
|
||||
})
|
@ -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()])
|
||||
}
|
||||
}
|
||||
|
501
test/unit/features/v3/setupTemplateRef.spec.ts
Normal file
501
test/unit/features/v3/setupTemplateRef.spec.ts
Normal file
@ -0,0 +1,501 @@
|
||||
import Vue from 'vue'
|
||||
import { ref, h, nextTick, reactive } from 'v3/index'
|
||||
|
||||
// reference: https://vue-composition-api-rfc.netlify.com/api.html#template-refs
|
||||
|
||||
describe('api: setup() template refs', () => {
|
||||
it('string ref mount', () => {
|
||||
const el = ref(null)
|
||||
|
||||
const Comp = {
|
||||
setup() {
|
||||
return {
|
||||
refKey: el
|
||||
}
|
||||
},
|
||||
render() {
|
||||
return h('div', { ref: 'refKey' })
|
||||
}
|
||||
}
|
||||
const vm = new Vue(Comp).$mount()
|
||||
expect(el.value).toBe(vm.$el)
|
||||
})
|
||||
|
||||
it('string ref update', async () => {
|
||||
const fooEl = ref(null)
|
||||
const barEl = ref(null)
|
||||
const refKey = ref('foo')
|
||||
|
||||
const Comp = {
|
||||
setup() {
|
||||
return {
|
||||
foo: fooEl,
|
||||
bar: barEl
|
||||
}
|
||||
},
|
||||
render() {
|
||||
return h('div', { ref: refKey.value })
|
||||
}
|
||||
}
|
||||
const vm = new Vue(Comp).$mount()
|
||||
expect(barEl.value).toBe(null)
|
||||
|
||||
refKey.value = 'bar'
|
||||
await nextTick()
|
||||
expect(fooEl.value).toBe(null)
|
||||
expect(barEl.value).toBe(vm.$el)
|
||||
})
|
||||
|
||||
it('string ref unmount', async () => {
|
||||
const el = ref(null)
|
||||
const toggle = ref(true)
|
||||
|
||||
const Comp = {
|
||||
setup() {
|
||||
return {
|
||||
refKey: el
|
||||
}
|
||||
},
|
||||
render() {
|
||||
return toggle.value ? h('div', { ref: 'refKey' }) : null
|
||||
}
|
||||
}
|
||||
|
||||
const vm = new Vue(Comp).$mount()
|
||||
expect(el.value).toBe(vm.$el)
|
||||
|
||||
toggle.value = false
|
||||
await nextTick()
|
||||
expect(el.value).toBe(null)
|
||||
})
|
||||
|
||||
it('function ref mount', () => {
|
||||
const fn = vi.fn()
|
||||
|
||||
const Comp = {
|
||||
render: () => h('div', { ref: fn })
|
||||
}
|
||||
const vm = new Vue(Comp).$mount()
|
||||
expect(fn.mock.calls[0][0]).toBe(vm.$el)
|
||||
})
|
||||
|
||||
it('function ref update', async () => {
|
||||
const fn1 = vi.fn()
|
||||
const fn2 = vi.fn()
|
||||
const fn = ref(fn1)
|
||||
|
||||
const Comp = { render: () => h('div', { ref: fn.value }) }
|
||||
|
||||
const vm = new Vue(Comp).$mount()
|
||||
expect(fn1.mock.calls).toHaveLength(1)
|
||||
expect(fn1.mock.calls[0][0]).toBe(vm.$el)
|
||||
expect(fn2.mock.calls).toHaveLength(0)
|
||||
|
||||
fn.value = fn2
|
||||
await nextTick()
|
||||
expect(fn1.mock.calls).toHaveLength(2)
|
||||
expect(fn1.mock.calls[1][0]).toBe(null)
|
||||
expect(fn2.mock.calls).toHaveLength(1)
|
||||
expect(fn2.mock.calls[0][0]).toBe(vm.$el)
|
||||
})
|
||||
|
||||
it('function ref unmount', async () => {
|
||||
const fn = vi.fn()
|
||||
const toggle = ref(true)
|
||||
|
||||
const Comp = {
|
||||
render: () => (toggle.value ? h('div', { ref: fn }) : null)
|
||||
}
|
||||
const vm = new Vue(Comp).$mount()
|
||||
expect(fn.mock.calls[0][0]).toBe(vm.$el)
|
||||
toggle.value = false
|
||||
await nextTick()
|
||||
expect(fn.mock.calls[1][0]).toBe(null)
|
||||
})
|
||||
|
||||
it('render function ref mount', () => {
|
||||
const el = ref(null)
|
||||
|
||||
const Comp = {
|
||||
setup() {
|
||||
return () => h('div', { ref: el })
|
||||
}
|
||||
}
|
||||
const vm = new Vue(Comp).$mount()
|
||||
expect(el.value).toBe(vm.$el)
|
||||
})
|
||||
|
||||
it('render function ref update', async () => {
|
||||
const refs = {
|
||||
foo: ref(null),
|
||||
bar: ref(null)
|
||||
}
|
||||
const refKey = ref<keyof typeof refs>('foo')
|
||||
|
||||
const Comp = {
|
||||
setup() {
|
||||
return () => h('div', { ref: refs[refKey.value] })
|
||||
}
|
||||
}
|
||||
const vm = new Vue(Comp).$mount()
|
||||
expect(refs.foo.value).toBe(vm.$el)
|
||||
expect(refs.bar.value).toBe(null)
|
||||
|
||||
refKey.value = 'bar'
|
||||
await nextTick()
|
||||
expect(refs.foo.value).toBe(null)
|
||||
expect(refs.bar.value).toBe(vm.$el)
|
||||
})
|
||||
|
||||
it('render function ref unmount', async () => {
|
||||
const el = ref(null)
|
||||
const toggle = ref(true)
|
||||
|
||||
const Comp = {
|
||||
setup() {
|
||||
return () => (toggle.value ? h('div', { ref: el }) : null)
|
||||
}
|
||||
}
|
||||
const vm = new Vue(Comp).$mount()
|
||||
expect(el.value).toBe(vm.$el)
|
||||
|
||||
toggle.value = false
|
||||
await nextTick()
|
||||
expect(el.value).toBe(null)
|
||||
})
|
||||
|
||||
it('string ref inside slots', async () => {
|
||||
const spy = vi.fn()
|
||||
const Child = {
|
||||
render(this: any) {
|
||||
return this.$slots.default
|
||||
}
|
||||
}
|
||||
|
||||
const Comp = {
|
||||
render() {
|
||||
return h(Child, [h('div', { ref: 'foo' })])
|
||||
},
|
||||
mounted(this: any) {
|
||||
spy(this.$refs.foo.tagName)
|
||||
}
|
||||
}
|
||||
new Vue(Comp).$mount()
|
||||
expect(spy).toHaveBeenCalledWith('DIV')
|
||||
})
|
||||
|
||||
it('string ref inside scoped slots', async () => {
|
||||
const spy = vi.fn()
|
||||
const Child = {
|
||||
render(this: any) {
|
||||
return this.$scopedSlots.default()
|
||||
}
|
||||
}
|
||||
|
||||
const Comp = {
|
||||
render() {
|
||||
return h(Child, {
|
||||
scopedSlots: {
|
||||
default: () => [h('div', { ref: 'foo' })]
|
||||
}
|
||||
})
|
||||
},
|
||||
mounted(this: any) {
|
||||
spy(this.$refs.foo.tagName)
|
||||
}
|
||||
}
|
||||
new Vue(Comp).$mount()
|
||||
expect(spy).toHaveBeenCalledWith('DIV')
|
||||
})
|
||||
|
||||
it('should work with direct reactive property', () => {
|
||||
const state = reactive({
|
||||
refKey: null
|
||||
})
|
||||
|
||||
const Comp = {
|
||||
setup() {
|
||||
return state
|
||||
},
|
||||
render() {
|
||||
return h('div', { ref: 'refKey' })
|
||||
}
|
||||
}
|
||||
const vm = new Vue(Comp).$mount()
|
||||
expect(state.refKey).toBe(vm.$el)
|
||||
})
|
||||
|
||||
test('multiple refs', () => {
|
||||
const refKey1 = ref(null)
|
||||
const refKey2 = ref(null)
|
||||
const refKey3 = ref(null)
|
||||
|
||||
const Comp = {
|
||||
setup() {
|
||||
return {
|
||||
refKey1,
|
||||
refKey2,
|
||||
refKey3
|
||||
}
|
||||
},
|
||||
render() {
|
||||
return h('div', [
|
||||
h('div', { ref: 'refKey1' }),
|
||||
h('div', { ref: 'refKey2' }),
|
||||
h('div', { ref: 'refKey3' })
|
||||
])
|
||||
}
|
||||
}
|
||||
const vm = new Vue(Comp).$mount()
|
||||
expect(refKey1.value).toBe(vm.$el.children[0])
|
||||
expect(refKey2.value).toBe(vm.$el.children[1])
|
||||
expect(refKey3.value).toBe(vm.$el.children[2])
|
||||
})
|
||||
|
||||
// vuejs/core#1505
|
||||
test('reactive template ref in the same template', async () => {
|
||||
const Comp = {
|
||||
setup() {
|
||||
const el = ref()
|
||||
return { el }
|
||||
},
|
||||
render(this: any) {
|
||||
return h(
|
||||
'div',
|
||||
{ attrs: { id: 'foo' }, ref: 'el' },
|
||||
this.el && this.el.id
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const vm = new Vue(Comp).$mount()
|
||||
// ref not ready on first render, but should queue an update immediately
|
||||
expect(vm.$el.outerHTML).toBe(`<div id="foo"></div>`)
|
||||
await nextTick()
|
||||
// ref should be updated
|
||||
expect(vm.$el.outerHTML).toBe(`<div id="foo">foo</div>`)
|
||||
})
|
||||
|
||||
// vuejs/core#1834
|
||||
test('exchange refs', async () => {
|
||||
const refToggle = ref(false)
|
||||
const spy = vi.fn()
|
||||
|
||||
const Comp = {
|
||||
render(this: any) {
|
||||
return h('div', [
|
||||
h('p', { ref: refToggle.value ? 'foo' : 'bar' }),
|
||||
h('i', { ref: refToggle.value ? 'bar' : 'foo' })
|
||||
])
|
||||
},
|
||||
mounted(this: any) {
|
||||
spy(this.$refs.foo.tagName, this.$refs.bar.tagName)
|
||||
},
|
||||
updated(this: any) {
|
||||
spy(this.$refs.foo.tagName, this.$refs.bar.tagName)
|
||||
}
|
||||
}
|
||||
|
||||
new Vue(Comp).$mount()
|
||||
|
||||
expect(spy.mock.calls[0][0]).toBe('I')
|
||||
expect(spy.mock.calls[0][1]).toBe('P')
|
||||
refToggle.value = true
|
||||
await nextTick()
|
||||
expect(spy.mock.calls[1][0]).toBe('P')
|
||||
expect(spy.mock.calls[1][1]).toBe('I')
|
||||
})
|
||||
|
||||
// vuejs/core#1789
|
||||
test('toggle the same ref to different elements', async () => {
|
||||
const refToggle = ref(false)
|
||||
const spy = vi.fn()
|
||||
|
||||
const Comp = {
|
||||
render(this: any) {
|
||||
return refToggle.value ? h('p', { ref: 'foo' }) : h('i', { ref: 'foo' })
|
||||
},
|
||||
mounted(this: any) {
|
||||
spy(this.$refs.foo.tagName)
|
||||
},
|
||||
updated(this: any) {
|
||||
spy(this.$refs.foo.tagName)
|
||||
}
|
||||
}
|
||||
|
||||
new Vue(Comp).$mount()
|
||||
|
||||
expect(spy.mock.calls[0][0]).toBe('I')
|
||||
refToggle.value = true
|
||||
await nextTick()
|
||||
expect(spy.mock.calls[1][0]).toBe('P')
|
||||
})
|
||||
|
||||
// vuejs/core#2078
|
||||
// @discrepancy Vue 2 doesn't handle merge refs
|
||||
// test('handling multiple merged refs', async () => {
|
||||
// const Foo = {
|
||||
// render: () => h('div', 'foo')
|
||||
// }
|
||||
// const Bar = {
|
||||
// render: () => h('div', 'bar')
|
||||
// }
|
||||
|
||||
// const viewRef = shallowRef<any>(Foo)
|
||||
// const elRef1 = ref()
|
||||
// const elRef2 = ref()
|
||||
|
||||
// const App = {
|
||||
// render() {
|
||||
// if (!viewRef.value) {
|
||||
// return null
|
||||
// }
|
||||
// const view = h(viewRef.value, { ref: elRef1 })
|
||||
// return h(view, { ref: elRef2 })
|
||||
// }
|
||||
// }
|
||||
|
||||
// new Vue(App).$mount()
|
||||
|
||||
// expect(elRef1.value.$el.innerHTML).toBe('foo')
|
||||
// expect(elRef1.value).toBe(elRef2.value)
|
||||
|
||||
// viewRef.value = Bar
|
||||
// await nextTick()
|
||||
// expect(elRef1.value.$el.innerHTML).toBe('bar')
|
||||
// expect(elRef1.value).toBe(elRef2.value)
|
||||
|
||||
// viewRef.value = null
|
||||
// await nextTick()
|
||||
// expect(elRef1.value).toBeNull()
|
||||
// expect(elRef1.value).toBe(elRef2.value)
|
||||
// })
|
||||
|
||||
// compiled output of <script setup> inline mode
|
||||
test('raw ref with ref_key', () => {
|
||||
let refs: any
|
||||
|
||||
const el = ref()
|
||||
|
||||
const App = {
|
||||
mounted() {
|
||||
refs = (this as any).$refs
|
||||
},
|
||||
render() {
|
||||
return h(
|
||||
'div',
|
||||
{
|
||||
ref: el,
|
||||
ref_key: 'el'
|
||||
},
|
||||
'hello'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
new Vue(App).$mount()
|
||||
|
||||
expect(el.value.innerHTML).toBe('hello')
|
||||
expect(refs.el.innerHTML).toBe('hello')
|
||||
})
|
||||
|
||||
// compiled output of v-for + template ref
|
||||
test('ref in v-for', async () => {
|
||||
const show = ref(true)
|
||||
const state = reactive({ list: [1, 2, 3] })
|
||||
const listRefs = ref<any[]>([])
|
||||
const mapRefs = () => listRefs.value.map(n => n.innerHTML)
|
||||
|
||||
const App = {
|
||||
render() {
|
||||
return show.value
|
||||
? h(
|
||||
'ul',
|
||||
state.list.map(i =>
|
||||
h(
|
||||
'li',
|
||||
{
|
||||
ref: listRefs,
|
||||
refInFor: true
|
||||
},
|
||||
i
|
||||
)
|
||||
)
|
||||
)
|
||||
: null
|
||||
}
|
||||
}
|
||||
|
||||
new Vue(App).$mount()
|
||||
|
||||
expect(mapRefs()).toMatchObject(['1', '2', '3'])
|
||||
|
||||
state.list.push(4)
|
||||
await nextTick()
|
||||
expect(mapRefs()).toMatchObject(['1', '2', '3', '4'])
|
||||
|
||||
state.list.shift()
|
||||
await nextTick()
|
||||
expect(mapRefs()).toMatchObject(['2', '3', '4'])
|
||||
|
||||
show.value = !show.value
|
||||
await nextTick()
|
||||
|
||||
expect(mapRefs()).toMatchObject([])
|
||||
|
||||
show.value = !show.value
|
||||
await nextTick()
|
||||
expect(mapRefs()).toMatchObject(['2', '3', '4'])
|
||||
})
|
||||
|
||||
test('named ref in v-for', async () => {
|
||||
const show = ref(true)
|
||||
const state = reactive({ list: [1, 2, 3] })
|
||||
const listRefs = ref([])
|
||||
const mapRefs = () => listRefs.value.map((n: HTMLElement) => n.innerHTML)
|
||||
|
||||
const App = {
|
||||
setup() {
|
||||
return { listRefs }
|
||||
},
|
||||
render() {
|
||||
return show.value
|
||||
? h(
|
||||
'ul',
|
||||
state.list.map(i =>
|
||||
h(
|
||||
'li',
|
||||
{
|
||||
ref: 'listRefs',
|
||||
refInFor: true
|
||||
},
|
||||
i
|
||||
)
|
||||
)
|
||||
)
|
||||
: null
|
||||
}
|
||||
}
|
||||
|
||||
new Vue(App).$mount()
|
||||
|
||||
expect(mapRefs()).toMatchObject(['1', '2', '3'])
|
||||
|
||||
state.list.push(4)
|
||||
await nextTick()
|
||||
expect(mapRefs()).toMatchObject(['1', '2', '3', '4'])
|
||||
|
||||
state.list.shift()
|
||||
await nextTick()
|
||||
expect(mapRefs()).toMatchObject(['2', '3', '4'])
|
||||
|
||||
show.value = !show.value
|
||||
await nextTick()
|
||||
|
||||
expect(mapRefs()).toMatchObject([])
|
||||
|
||||
show.value = !show.value
|
||||
await nextTick()
|
||||
expect(mapRefs()).toMatchObject(['2', '3', '4'])
|
||||
})
|
||||
})
|
3
typescript/vnode.d.ts
vendored
3
typescript/vnode.d.ts
vendored
@ -1,4 +1,5 @@
|
||||
import VNode from '../src/core/vdom/vnode'
|
||||
import { Ref } from '../src/v3'
|
||||
import { Component } from './component'
|
||||
|
||||
declare type VNodeChildren =
|
||||
@ -56,7 +57,7 @@ declare type VNodeWithData = VNode & {
|
||||
declare interface VNodeData {
|
||||
key?: string | number
|
||||
slot?: string
|
||||
ref?: string
|
||||
ref?: string | Ref | ((el: any) => void)
|
||||
is?: string
|
||||
pre?: boolean
|
||||
tag?: string
|
||||
|
Loading…
Reference in New Issue
Block a user