mirror of
https://gitee.com/vuejs/vue.git
synced 2024-12-02 12:07:39 +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,
|
slotName?: ?string,
|
||||||
slotTarget?: ?string,
|
slotTarget?: ?string,
|
||||||
|
|
||||||
|
ref?: string,
|
||||||
|
refInFor?: boolean,
|
||||||
|
|
||||||
render?: true,
|
render?: true,
|
||||||
renderMethod?: ?string,
|
renderMethod?: ?string,
|
||||||
renderArgs?: ?string,
|
renderArgs?: ?string,
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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},`
|
||||||
|
@ -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)`)
|
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import directives from './directives'
|
import directives from './directives'
|
||||||
|
import ref from './ref'
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
|
ref,
|
||||||
directives
|
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')
|
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:
|
||||||
|
@ -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}))})])}`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user