mirror of
https://gitee.com/vuejs/vue.git
synced 2024-11-30 11:07:51 +08:00
make ref a runtime module
This commit is contained in:
parent
8f274a3324
commit
060fab9ec1
@ -63,6 +63,9 @@ declare type ASTElement = {
|
||||
slotName?: ?string,
|
||||
slotTarget?: ?string,
|
||||
|
||||
ref?: string,
|
||||
refInFor?: boolean,
|
||||
|
||||
render?: true,
|
||||
renderMethod?: ?string,
|
||||
renderArgs?: ?string,
|
||||
|
@ -100,13 +100,6 @@ declare interface Component {
|
||||
val: any,
|
||||
render: Function
|
||||
) => ?Array<VNode>;
|
||||
// registerRef
|
||||
_r: (
|
||||
key: string,
|
||||
ref: Component | Element,
|
||||
vFor: boolean,
|
||||
isRemoval: boolean
|
||||
) => void;
|
||||
// apply v-bind object
|
||||
_b: (vnode: VNodeWithData, value: any) => void;
|
||||
|
||||
|
@ -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},`
|
||||
|
@ -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)`)
|
||||
}
|
||||
|
@ -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<Component>) {
|
||||
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) {
|
||||
|
@ -1,5 +1,7 @@
|
||||
import directives from './directives'
|
||||
import ref from './ref'
|
||||
|
||||
export default [
|
||||
ref,
|
||||
directives
|
||||
]
|
||||
|
38
src/core/vdom/modules/ref.js
Normal file
38
src/core/vdom/modules/ref.js
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
@ -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:
|
||||
|
@ -62,14 +62,14 @@ describe('codegen', () => {
|
||||
it('generate v-ref directive', () => {
|
||||
assertCodegen(
|
||||
'<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', () => {
|
||||
assertCodegen(
|
||||
'<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}))})])}`
|
||||
)
|
||||
})
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user