make ref a runtime module

This commit is contained in:
Evan You 2016-06-01 19:52:42 -04:00
parent 8f274a3324
commit 060fab9ec1
9 changed files with 77 additions and 51 deletions

View File

@ -63,6 +63,9 @@ declare type ASTElement = {
slotName?: ?string, slotName?: ?string,
slotTarget?: ?string, slotTarget?: ?string,
ref?: string,
refInFor?: boolean,
render?: true, render?: true,
renderMethod?: ?string, renderMethod?: ?string,
renderArgs?: ?string, renderArgs?: ?string,

View File

@ -100,13 +100,6 @@ declare interface Component {
val: any, val: any,
render: Function render: Function
) => ?Array<VNode>; ) => ?Array<VNode>;
// registerRef
_r: (
key: string,
ref: Component | Element,
vFor: boolean,
isRemoval: boolean
) => void;
// apply v-bind object // apply v-bind object
_b: (vnode: VNodeWithData, value: any) => void; _b: (vnode: VNodeWithData, value: any) => void;

View File

@ -112,6 +112,13 @@ function genData (el: ASTElement): string | void {
if (el.key) { if (el.key) {
data += `key:${el.key},` data += `key:${el.key},`
} }
// ref
if (el.ref) {
data += `ref:"${el.ref}",`
}
if (el.refInFor) {
data += `refInFor:true,`
}
// slot target // slot target
if (el.slotTarget) { if (el.slotTarget) {
data += `slot:${el.slotTarget},` data += `slot:${el.slotTarget},`

View File

@ -1,19 +1,15 @@
/* @flow */ /* @flow */
import { addHook } from '../helpers'
export default function ref (el: ASTElement, dir: ASTDirective) { export default function ref (el: ASTElement, dir: ASTDirective) {
// go up and check if this node is inside a v-for if (dir.arg) {
let isFor = false el.ref = dir.arg
let parent = el // go up and check if this node is inside a v-for
while (parent) { let parent = el
if (parent.for !== undefined) { while (parent) {
isFor = true 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)`)
} }

View File

@ -4,7 +4,7 @@ import config from '../config'
import VNode, { emptyVNode } from '../vdom/vnode' import VNode, { emptyVNode } from '../vdom/vnode'
import { normalizeChildren } from '../vdom/helpers' import { normalizeChildren } from '../vdom/helpers'
import { import {
warn, bind, remove, isObject, toObject, warn, bind, isObject, toObject,
nextTick, resolveAsset, renderString nextTick, resolveAsset, renderString
} from '../util/index' } from '../util/index'
@ -120,34 +120,6 @@ export function renderMixin (Vue: Class<Component>) {
return ret 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 // apply v-bind object
Vue.prototype._b = function (vnode: VNodeWithData, value: any) { Vue.prototype._b = function (vnode: VNodeWithData, value: any) {
if (value) { if (value) {

View File

@ -1,5 +1,7 @@
import directives from './directives' import directives from './directives'
import ref from './ref'
export default [ export default [
ref,
directives directives
] ]

View File

@ -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
}
}
}

View File

@ -25,6 +25,21 @@ describe('Directive v-ref', () => {
expect(vm.$refs['test-hyphen'].$options.id).toBe('test2') 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', () => { it('should accept camelCase refs', () => {
const vm = new Vue({ const vm = new Vue({
template: template:

View File

@ -62,14 +62,14 @@ describe('codegen', () => {
it('generate v-ref directive', () => { it('generate v-ref directive', () => {
assertCodegen( assertCodegen(
'<p v-ref:component1></p>', '<p v-ref:component1></p>',
`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', () => { it('generate v-ref directive on v-for', () => {
assertCodegen( assertCodegen(
'<ul><li v-for="item in items" v-ref:component1></li></ul>', '<ul><li v-for="item in items" v-ref:component1></li></ul>',
`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}))})])}`
) )
}) })