mirror of
https://gitee.com/vuejs/vue.git
synced 2024-11-30 02:57:43 +08:00
fix(core): handle edge cases for functional component returning arrays
fix #7282
This commit is contained in:
parent
49aae6bb15
commit
8335217cb4
@ -107,7 +107,7 @@ export function createComponent (
|
||||
context: Component,
|
||||
children: ?Array<VNode>,
|
||||
tag?: string
|
||||
): VNode | void {
|
||||
): VNode | Array<VNode> | void {
|
||||
if (isUndef(Ctor)) {
|
||||
return
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ export function createElement (
|
||||
children: any,
|
||||
normalizationType: any,
|
||||
alwaysNormalize: boolean
|
||||
): VNode {
|
||||
): VNode | Array<VNode> {
|
||||
if (Array.isArray(data) || isPrimitive(data)) {
|
||||
normalizationType = children
|
||||
children = data
|
||||
@ -48,7 +48,7 @@ export function _createElement (
|
||||
data?: VNodeData,
|
||||
children?: any,
|
||||
normalizationType?: number
|
||||
): VNode {
|
||||
): VNode | Array<VNode> {
|
||||
if (isDef(data) && isDef((data: any).__ob__)) {
|
||||
process.env.NODE_ENV !== 'production' && warn(
|
||||
`Avoid using observed data object as vnode data: ${JSON.stringify(data)}\n` +
|
||||
@ -117,7 +117,9 @@ export function _createElement (
|
||||
vnode = createComponent(tag, data, context, children)
|
||||
}
|
||||
if (isDef(vnode)) {
|
||||
if (ns) applyNS(vnode, ns)
|
||||
if (ns && !Array.isArray(vnode)) {
|
||||
applyNS(vnode, ns)
|
||||
}
|
||||
return vnode
|
||||
} else {
|
||||
return createEmptyVNode()
|
||||
|
@ -3,6 +3,7 @@
|
||||
import VNode from './vnode'
|
||||
import { createElement } from './create-element'
|
||||
import { resolveInject } from '../instance/inject'
|
||||
import { normalizeChildren } from '../vdom/helpers/normalize-children'
|
||||
import { resolveSlots } from '../instance/render-helpers/resolve-slots'
|
||||
import { installRenderHelpers } from '../instance/render-helpers/index'
|
||||
|
||||
@ -47,8 +48,8 @@ function FunctionalRenderContext (
|
||||
|
||||
if (options._scopeId) {
|
||||
this._c = (a, b, c, d) => {
|
||||
const vnode: ?VNode = createElement(contextVm, a, b, c, d, needNormalization)
|
||||
if (vnode) {
|
||||
const vnode = createElement(contextVm, a, b, c, d, needNormalization)
|
||||
if (vnode && !Array.isArray(vnode)) {
|
||||
vnode.fnScopeId = options._scopeId
|
||||
vnode.fnContext = parent
|
||||
}
|
||||
@ -67,7 +68,7 @@ export function createFunctionalComponent (
|
||||
data: VNodeData,
|
||||
contextVm: Component,
|
||||
children: ?Array<VNode>
|
||||
): VNode | void {
|
||||
): VNode | Array<VNode> | void {
|
||||
const options = Ctor.options
|
||||
const props = {}
|
||||
const propOptions = options.props
|
||||
@ -91,14 +92,23 @@ export function createFunctionalComponent (
|
||||
const vnode = options.render.call(null, renderContext._c, renderContext)
|
||||
|
||||
if (vnode instanceof VNode) {
|
||||
vnode.fnContext = contextVm
|
||||
vnode.fnOptions = options
|
||||
if (data.slot) {
|
||||
(vnode.data || (vnode.data = {})).slot = data.slot
|
||||
setFunctionalContextForVNode(vnode, data, contextVm, options)
|
||||
return vnode
|
||||
} else if (Array.isArray(vnode)) {
|
||||
const vnodes = normalizeChildren(vnode) || []
|
||||
for (let i = 0; i < vnodes.length; i++) {
|
||||
setFunctionalContextForVNode(vnodes[i], data, contextVm, options)
|
||||
}
|
||||
return vnodes
|
||||
}
|
||||
}
|
||||
|
||||
return vnode
|
||||
function setFunctionalContextForVNode (vnode, data, vm, options) {
|
||||
vnode.fnContext = vm
|
||||
vnode.fnOptions = options
|
||||
if (data.slot) {
|
||||
(vnode.data || (vnode.data = {})).slot = data.slot
|
||||
}
|
||||
}
|
||||
|
||||
function mergeProps (to, from) {
|
||||
|
@ -186,6 +186,62 @@ describe('Options functional', () => {
|
||||
expect(vnode).toEqual(createEmptyVNode())
|
||||
})
|
||||
|
||||
// #7282
|
||||
it('should normalize top-level arrays', () => {
|
||||
const Foo = {
|
||||
functional: true,
|
||||
render (h) {
|
||||
return [h('span', 'hi'), null]
|
||||
}
|
||||
}
|
||||
const vm = new Vue({
|
||||
template: `<div><foo/></div>`,
|
||||
components: { Foo }
|
||||
}).$mount()
|
||||
expect(vm.$el.innerHTML).toBe('<span>hi</span>')
|
||||
})
|
||||
|
||||
it('should work when used as named slot and returning array', () => {
|
||||
const Foo = {
|
||||
template: `<div><slot name="test"/></div>`
|
||||
}
|
||||
|
||||
const Bar = {
|
||||
functional: true,
|
||||
render: h => ([
|
||||
h('div', 'one'),
|
||||
h('div', 'two'),
|
||||
h(Baz)
|
||||
])
|
||||
}
|
||||
|
||||
const Baz = {
|
||||
functional: true,
|
||||
render: h => h('div', 'three')
|
||||
}
|
||||
|
||||
const vm = new Vue({
|
||||
template: `<foo><bar slot="test"/></foo>`,
|
||||
components: { Foo, Bar }
|
||||
}).$mount()
|
||||
|
||||
expect(vm.$el.innerHTML).toBe('<div>one</div><div>two</div><div>three</div>')
|
||||
})
|
||||
|
||||
it('should apply namespace when returning arrays', () => {
|
||||
const Child = {
|
||||
functional: true,
|
||||
render: h => ([h('foo'), h('bar')])
|
||||
}
|
||||
const vm = new Vue({
|
||||
template: `<svg><child/></svg>`,
|
||||
components: { Child }
|
||||
}).$mount()
|
||||
|
||||
expect(vm.$el.childNodes[0].namespaceURI).toContain('svg')
|
||||
expect(vm.$el.childNodes[1].namespaceURI).toContain('svg')
|
||||
})
|
||||
|
||||
it('should work with render fns compiled from template', done => {
|
||||
// code generated via vue-template-es2015-compiler
|
||||
var render = function (_h, _vm) {
|
||||
|
Loading…
Reference in New Issue
Block a user