diff --git a/flow/component.js b/flow/component.js index b1d320b720..3c28f756a6 100644 --- a/flow/component.js +++ b/flow/component.js @@ -2,10 +2,6 @@ import type { Config } from '../src/core/config' import type VNode from '../src/core/vdom/vnode' import type Watcher from '../src/core/observer/watcher' -declare type Refs = { - [key: string]: Component | Element | Array | void; -}; - declare interface Component { // constructor information static cid: number; @@ -28,7 +24,7 @@ declare interface Component { $parent: Component | void; $root: Component; $children: Array; - $refs: Refs; + $refs: { [key: string]: Component | Element | Array | void }; $slots: { [key: string]: Array }; $scopedSlots: { [key: string]: () => VNodeChildren }; $vnode: VNode; diff --git a/src/core/instance/lifecycle.js b/src/core/instance/lifecycle.js index 1d1378c3c4..8a8a2c9b5f 100644 --- a/src/core/instance/lifecycle.js +++ b/src/core/instance/lifecycle.js @@ -3,7 +3,6 @@ import config from '../config' import { perf } from '../util/perf' import Watcher from '../observer/watcher' -import { resetRefs } from '../vdom/modules/ref' import { createEmptyVNode } from '../vdom/vnode' import { observerState } from '../observer/index' import { updateComponentListeners } from './events' @@ -56,7 +55,6 @@ export function lifecycleMixin (Vue: Class) { const prevVnode = vm._vnode const prevActiveInstance = activeInstance activeInstance = vm - vm.$refs = resetRefs(vm.$refs) vm._vnode = vnode // Vue.prototype.__patch__ is injected in entry points // based on the rendering backend used. diff --git a/src/core/vdom/modules/ref.js b/src/core/vdom/modules/ref.js index d700e9e52c..c69547ce7a 100644 --- a/src/core/vdom/modules/ref.js +++ b/src/core/vdom/modules/ref.js @@ -1,40 +1,44 @@ /* @flow */ +import { remove } from 'shared/util' + export default { - create: registerRef, - update: registerRef + create (_: any, vnode: VNodeWithData) { + registerRef(vnode) + }, + update (oldVnode: VNodeWithData, vnode: VNodeWithData) { + if (oldVnode.data.ref !== vnode.data.ref) { + registerRef(oldVnode, true) + registerRef(vnode) + } + }, + destroy (vnode: VNodeWithData) { + registerRef(vnode, true) + } } -export function registerRef (_: any, vnode: VNodeWithData) { +export function registerRef (vnode: VNodeWithData, isRemoval: ?boolean) { const key = vnode.data.ref if (!key) return + const vm = vnode.context const ref = vnode.componentInstance || vnode.elm - const refs = vnode.context.$refs - - if (typeof key === 'function') { - key(ref) - } else if (vnode.data.refInFor) { - const refArray = refs[key] - if (Array.isArray(refArray)) { - if (refArray.indexOf(ref) < 0) { - refArray.push(ref) - } - } else { - refs[key] = [ref] + const refs = vm.$refs + if (isRemoval) { + if (Array.isArray(refs[key])) { + remove(refs[key], ref) + } else if (refs[key] === ref) { + refs[key] = undefined } } else { - refs[key] = ref - } -} - -export function resetRefs (refs: Refs): Refs { - const res = {} - // keep existing v-for ref arrays even if empty - for (const key in refs) { - if (Array.isArray(refs[key])) { - res[key] = [] + if (vnode.data.refInFor) { + if (Array.isArray(refs[key]) && refs[key].indexOf(ref) < 0) { + refs[key].push(ref) + } else { + refs[key] = [ref] + } + } else { + refs[key] = ref } } - return res } diff --git a/src/core/vdom/patch.js b/src/core/vdom/patch.js index 27632aee5f..aac33dc49a 100644 --- a/src/core/vdom/patch.js +++ b/src/core/vdom/patch.js @@ -189,7 +189,7 @@ export function createPatchFunction (backend) { } else { // empty component root. // skip all element-related modules except for ref (#3455) - registerRef(null, vnode) + registerRef(vnode) // make sure to invoke the insert hook insertedVnodeQueue.push(vnode) } diff --git a/test/unit/features/ref.spec.js b/test/unit/features/ref.spec.js index 1aa6760b90..da637b7bfa 100644 --- a/test/unit/features/ref.spec.js +++ b/test/unit/features/ref.spec.js @@ -157,72 +157,4 @@ describe('ref', () => { }).$mount() expect(vm.$refs.test).toBe(vm.$children[0]) }) - - it('should should call callback method (v-for)', done => { - const vm = new Vue({ - data: { - items: [1, 2, 3] - }, - template: ` -
- -
- `, - components: { - test: { - props: ['n'], - template: '
{{ n }}
' - } - }, - methods: { - onRef (ref) { - (this.$refs.list || (this.$refs.list = [])).push(ref) - } - } - }).$mount() - assertRefs() - // updating - vm.items.push(4) - waitForUpdate(assertRefs) - .then(() => { vm.items = [] }) - .then(assertRefs) - .then(done) - - function assertRefs () { - expect(Array.isArray(vm.$refs.list)).toBe(true) - expect(vm.$refs.list.length).toBe(vm.items.length) - expect(vm.$refs.list.every((comp, i) => comp.$el.textContent === String(i + 1))).toBe(true) - } - }) - - it('should should call inline callback (v-for)', done => { - const vm = new Vue({ - data: { - items: [1, 2, 3] - }, - template: ` -
- -
- `, - components: { - test: { - props: ['n'], - template: '
{{ n }}
' - } - } - }).$mount() - assertRefs() - // updating - vm.items.push(4) - waitForUpdate(assertRefs) - .then(() => { vm.items = [] }) - .then(assertRefs) - .then(done) - - function assertRefs () { - expect(Object.keys(vm.$refs).length).toBe(vm.items.length) - expect(Object.keys(vm.$refs).every(i => vm.$refs[i].$el.textContent === String(i))).toBe(true) - } - }) })