Skip to content

Commit acec8db

Browse files
znckyyx990803
authored andcommitted
[WIP] Support for ref callback (#4807)
* ✨ Tests for ref callback * ✨ Support for ref callback * Add test of inline ref callback * adjust ref implementation strategy * fix patch ref registration * fix tests * fix flow * fix test for phantomjs
1 parent 8d88512 commit acec8db

File tree

5 files changed

+102
-32
lines changed

5 files changed

+102
-32
lines changed

flow/component.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ import type { Config } from '../src/core/config'
22
import type VNode from '../src/core/vdom/vnode'
33
import type Watcher from '../src/core/observer/watcher'
44

5+
declare type Refs = {
6+
[key: string]: Component | Element | Array<Component | Element> | void;
7+
};
8+
59
declare interface Component {
610
// constructor information
711
static cid: number;
@@ -23,7 +27,7 @@ declare interface Component {
2327
$parent: Component | void;
2428
$root: Component;
2529
$children: Array<Component>;
26-
$refs: { [key: string]: Component | Element | Array<Component | Element> | void };
30+
$refs: Refs;
2731
$slots: { [key: string]: Array<VNode> };
2832
$scopedSlots: { [key: string]: () => VNodeChildren };
2933
$vnode: VNode;

src/core/instance/lifecycle.js

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* @flow */
22

33
import Watcher from '../observer/watcher'
4+
import { resetRefs } from '../vdom/modules/ref'
45
import { createEmptyVNode } from '../vdom/vnode'
56
import { observerState } from '../observer/index'
67
import { updateComponentListeners } from './events'
@@ -83,6 +84,7 @@ export function lifecycleMixin (Vue: Class<Component>) {
8384
const prevVnode = vm._vnode
8485
const prevActiveInstance = activeInstance
8586
activeInstance = vm
87+
vm.$refs = resetRefs(vm.$refs)
8688
vm._vnode = vnode
8789
// Vue.prototype.__patch__ is injected in entry points
8890
// based on the rendering backend used.

src/core/vdom/modules/ref.js

+26-30
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,40 @@
11
/* @flow */
22

3-
import { remove } from 'shared/util'
4-
53
export default {
6-
create (_: any, vnode: VNodeWithData) {
7-
registerRef(vnode)
8-
},
9-
update (oldVnode: VNodeWithData, vnode: VNodeWithData) {
10-
if (oldVnode.data.ref !== vnode.data.ref) {
11-
registerRef(oldVnode, true)
12-
registerRef(vnode)
13-
}
14-
},
15-
destroy (vnode: VNodeWithData) {
16-
registerRef(vnode, true)
17-
}
4+
create: registerRef,
5+
update: registerRef
186
}
197

20-
export function registerRef (vnode: VNodeWithData, isRemoval: ?boolean) {
8+
export function registerRef (_: any, vnode: VNodeWithData) {
219
const key = vnode.data.ref
2210
if (!key) return
2311

24-
const vm = vnode.context
2512
const ref = vnode.componentInstance || vnode.elm
26-
const refs = vm.$refs
27-
if (isRemoval) {
28-
if (Array.isArray(refs[key])) {
29-
remove(refs[key], ref)
30-
} else if (refs[key] === ref) {
31-
refs[key] = undefined
32-
}
33-
} else {
34-
if (vnode.data.refInFor) {
35-
if (Array.isArray(refs[key]) && refs[key].indexOf(ref) < 0) {
36-
refs[key].push(ref)
37-
} else {
38-
refs[key] = [ref]
13+
const refs = vnode.context.$refs
14+
15+
if (typeof key === 'function') {
16+
key(ref)
17+
} else if (vnode.data.refInFor) {
18+
const refArray = refs[key]
19+
if (Array.isArray(refArray)) {
20+
if (refArray.indexOf(ref) < 0) {
21+
refArray.push(ref)
3922
}
4023
} else {
41-
refs[key] = ref
24+
refs[key] = [ref]
25+
}
26+
} else {
27+
refs[key] = ref
28+
}
29+
}
30+
31+
export function resetRefs (refs: Refs): Refs {
32+
const res = {}
33+
// keep existing v-for ref arrays even if empty
34+
for (const key in refs) {
35+
if (Array.isArray(refs[key])) {
36+
res[key] = []
4237
}
4338
}
39+
return res
4440
}

src/core/vdom/patch.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ export function createPatchFunction (backend) {
189189
} else {
190190
// empty component root.
191191
// skip all element-related modules except for ref (#3455)
192-
registerRef(vnode)
192+
registerRef(null, vnode)
193193
// make sure to invoke the insert hook
194194
insertedVnodeQueue.push(vnode)
195195
}

test/unit/features/ref.spec.js

+68
Original file line numberDiff line numberDiff line change
@@ -157,4 +157,72 @@ describe('ref', () => {
157157
}).$mount()
158158
expect(vm.$refs.test).toBe(vm.$children[0])
159159
})
160+
161+
it('should should call callback method (v-for)', done => {
162+
const vm = new Vue({
163+
data: {
164+
items: [1, 2, 3]
165+
},
166+
template: `
167+
<div>
168+
<test v-for="n in items" :key="n" :ref="onRef" :n="n"></test>
169+
</div>
170+
`,
171+
components: {
172+
test: {
173+
props: ['n'],
174+
template: '<div>{{ n }}</div>'
175+
}
176+
},
177+
methods: {
178+
onRef (ref) {
179+
(this.$refs.list || (this.$refs.list = [])).push(ref)
180+
}
181+
}
182+
}).$mount()
183+
assertRefs()
184+
// updating
185+
vm.items.push(4)
186+
waitForUpdate(assertRefs)
187+
.then(() => { vm.items = [] })
188+
.then(assertRefs)
189+
.then(done)
190+
191+
function assertRefs () {
192+
expect(Array.isArray(vm.$refs.list)).toBe(true)
193+
expect(vm.$refs.list.length).toBe(vm.items.length)
194+
expect(vm.$refs.list.every((comp, i) => comp.$el.textContent === String(i + 1))).toBe(true)
195+
}
196+
})
197+
198+
it('should should call inline callback (v-for)', done => {
199+
const vm = new Vue({
200+
data: {
201+
items: [1, 2, 3]
202+
},
203+
template: `
204+
<div>
205+
<test v-for="n in items" :key="n" :ref="function (ref) { $refs[n] = ref }" :n="n"></test>
206+
</div>
207+
`,
208+
components: {
209+
test: {
210+
props: ['n'],
211+
template: '<div>{{ n }}</div>'
212+
}
213+
}
214+
}).$mount()
215+
assertRefs()
216+
// updating
217+
vm.items.push(4)
218+
waitForUpdate(assertRefs)
219+
.then(() => { vm.items = [] })
220+
.then(assertRefs)
221+
.then(done)
222+
223+
function assertRefs () {
224+
expect(Object.keys(vm.$refs).length).toBe(vm.items.length)
225+
expect(Object.keys(vm.$refs).every(i => vm.$refs[i].$el.textContent === String(i))).toBe(true)
226+
}
227+
})
160228
})

0 commit comments

Comments
 (0)