From 060fab9ec1d7df09312fb301f092b6790cf1cd46 Mon Sep 17 00:00:00 2001 From: Evan You Date: Wed, 1 Jun 2016 19:52:42 -0400 Subject: [PATCH] make ref a runtime module --- flow/compiler.js | 3 ++ flow/component.js | 7 ---- src/compiler/codegen.js | 7 ++++ src/compiler/directives/ref.js | 22 +++++-------- src/core/instance/render.js | 30 +---------------- src/core/vdom/modules/index.js | 2 ++ src/core/vdom/modules/ref.js | 38 ++++++++++++++++++++++ test/unit/features/directives/ref.spec.js | 15 +++++++++ test/unit/modules/compiler/codegen.spec.js | 4 +-- 9 files changed, 77 insertions(+), 51 deletions(-) create mode 100644 src/core/vdom/modules/ref.js diff --git a/flow/compiler.js b/flow/compiler.js index 99606af3..77d27859 100644 --- a/flow/compiler.js +++ b/flow/compiler.js @@ -63,6 +63,9 @@ declare type ASTElement = { slotName?: ?string, slotTarget?: ?string, + ref?: string, + refInFor?: boolean, + render?: true, renderMethod?: ?string, renderArgs?: ?string, diff --git a/flow/component.js b/flow/component.js index 01206aa3..7debfac1 100644 --- a/flow/component.js +++ b/flow/component.js @@ -100,13 +100,6 @@ declare interface Component { val: any, render: Function ) => ?Array; - // registerRef - _r: ( - key: string, - ref: Component | Element, - vFor: boolean, - isRemoval: boolean - ) => void; // apply v-bind object _b: (vnode: VNodeWithData, value: any) => void; diff --git a/src/compiler/codegen.js b/src/compiler/codegen.js index 823f37fd..18d2fc65 100644 --- a/src/compiler/codegen.js +++ b/src/compiler/codegen.js @@ -112,6 +112,13 @@ function genData (el: ASTElement): string | void { if (el.key) { data += `key:${el.key},` } + // ref + if (el.ref) { + data += `ref:"${el.ref}",` + } + if (el.refInFor) { + data += `refInFor:true,` + } // slot target if (el.slotTarget) { data += `slot:${el.slotTarget},` diff --git a/src/compiler/directives/ref.js b/src/compiler/directives/ref.js index e771b35d..ee420eca 100644 --- a/src/compiler/directives/ref.js +++ b/src/compiler/directives/ref.js @@ -1,19 +1,15 @@ /* @flow */ -import { addHook } from '../helpers' - export default function ref (el: ASTElement, dir: ASTDirective) { - // go up and check if this node is inside a v-for - let isFor = false - let parent = el - while (parent) { - if (parent.for !== undefined) { - isFor = true + if (dir.arg) { + el.ref = dir.arg + // go up and check if this node is inside a v-for + let parent = el + while (parent) { + if (parent.for !== undefined) { + el.refInFor = true + } + parent = parent.parent } - parent = parent.parent } - // registerRef: _r(name, ref, vFor?, remove?) - const code = `_r("${dir.arg}",n1.child||n1.elm,${isFor ? 'true' : 'false'}` - addHook(el, 'insert', `${code},false)`) - addHook(el, 'destroy', `${code},true)`) } diff --git a/src/core/instance/render.js b/src/core/instance/render.js index 902b9624..bfbe0f4f 100644 --- a/src/core/instance/render.js +++ b/src/core/instance/render.js @@ -4,7 +4,7 @@ import config from '../config' import VNode, { emptyVNode } from '../vdom/vnode' import { normalizeChildren } from '../vdom/helpers' import { - warn, bind, remove, isObject, toObject, + warn, bind, isObject, toObject, nextTick, resolveAsset, renderString } from '../util/index' @@ -120,34 +120,6 @@ export function renderMixin (Vue: Class) { return ret } - // register ref - Vue.prototype._r = function ( - key: string, - ref: Vue | Element, - vFor: boolean, - isRemoval: boolean - ) { - const vm: Component = this - const refs = vm.$refs - if (isRemoval) { - if (Array.isArray(refs[key])) { - remove(refs[key], ref) - } else { - refs[key] = undefined - } - } else { - if (vFor) { - if (Array.isArray(refs[key])) { - refs[key].push(ref) - } else { - refs[key] = [ref] - } - } else { - refs[key] = ref - } - } - } - // apply v-bind object Vue.prototype._b = function (vnode: VNodeWithData, value: any) { if (value) { diff --git a/src/core/vdom/modules/index.js b/src/core/vdom/modules/index.js index 1f3799b6..727c8646 100644 --- a/src/core/vdom/modules/index.js +++ b/src/core/vdom/modules/index.js @@ -1,5 +1,7 @@ import directives from './directives' +import ref from './ref' export default [ + ref, directives ] diff --git a/src/core/vdom/modules/ref.js b/src/core/vdom/modules/ref.js new file mode 100644 index 00000000..b12f61e2 --- /dev/null +++ b/src/core/vdom/modules/ref.js @@ -0,0 +1,38 @@ +/* flow */ + +import { remove } from 'shared/util' + +export default { + create (_, vnode) { + registerRef(vnode, false) + }, + destroy (vnode) { + registerRef(vnode, true) + } +} + +function registerRef (vnode: VNodeWithData, isRemoval: boolean) { + const key = vnode.data.ref + if (!key) return + + const vm = vnode.context + const ref = vnode.child || vnode.elm + const refs = vm.$refs + if (isRemoval) { + if (Array.isArray(refs[key])) { + remove(refs[key], ref) + } else if (refs[key] === ref) { + refs[key] = undefined + } + } else { + if (vnode.data.refInFor) { + if (Array.isArray(refs[key])) { + refs[key].push(ref) + } else { + refs[key] = [ref] + } + } else { + refs[key] = ref + } + } +} diff --git a/test/unit/features/directives/ref.spec.js b/test/unit/features/directives/ref.spec.js index 0a605f8f..f27be012 100644 --- a/test/unit/features/directives/ref.spec.js +++ b/test/unit/features/directives/ref.spec.js @@ -25,6 +25,21 @@ describe('Directive v-ref', () => { expect(vm.$refs['test-hyphen'].$options.id).toBe('test2') }) + it('should work as a hyperscript prop', () => { + const vm = new Vue({ + components, + render () { + const h = this.$createElement + return h('div', null, [ + h('test', { ref: 'test' }) + ]) + } + }) + vm.$mount() + expect(vm.$refs.test).toBeTruthy() + expect(vm.$refs.test.$options.id).toBe('test') + }) + it('should accept camelCase refs', () => { const vm = new Vue({ template: diff --git a/test/unit/modules/compiler/codegen.spec.js b/test/unit/modules/compiler/codegen.spec.js index ca6997cb..106516e6 100644 --- a/test/unit/modules/compiler/codegen.spec.js +++ b/test/unit/modules/compiler/codegen.spec.js @@ -62,14 +62,14 @@ describe('codegen', () => { it('generate v-ref directive', () => { assertCodegen( '

', - `with(this){return _h(_e('p',{hook:{"insert":function(n1,n2){_r("component1",n1.child||n1.elm,false,false)},"destroy":function(n1,n2){_r("component1",n1.child||n1.elm,false,true)}}}))}` + `with(this){return _h(_e('p',{ref:"component1"}))}` ) }) it('generate v-ref directive on v-for', () => { assertCodegen( '
', - `with(this){return _h(_e('ul'),[(items)&&_l((items),function(item,$index,$key){return _h(_e('li',{hook:{"insert":function(n1,n2){_r("component1",n1.child||n1.elm,true,false)},"destroy":function(n1,n2){_r("component1",n1.child||n1.elm,true,true)}}}))})])}` + `with(this){return _h(_e('ul'),[(items)&&_l((items),function(item,$index,$key){return _h(_e('li',{ref:"component1",refInFor:true}))})])}` ) })