Vue.js渲染函数是否允许返回VNode数组



我正在扩展Vue.js前端应用程序。我当前正在检查功能组件中的渲染功能。在查看了这些文档后,我现在了解到,功能组件中的render函数将返回用CreateElement创建的单个VNode,也就是h。当我看到一个VNode作为数组中的一个元素返回时,我感到很困惑。我在文档中找不到任何对此语法的引用。有人有什么见解吗?

export default {
name: 'OfferModule',
functional: true,
props: {
data: Object,
placementInt: Number,
len: Number
},
render (h, ctx) {
let adunitTheme = []
const isDev = str => (process.env.dev ? str : '')
const i = parseInt(ctx.props.placementInt)
const isDevice = ctx.props.data.Component === 'Device'
const Component = isDevice ? Device : Adunit
/* device helper classes */
const adunitWrapper = ctx.props.data.Decorate?.CodeName === 'AdunitWrapper'
if (!isDevice /* is Adunit */) {
const offerTypeInt = ctx.props.data.OfferType
adunitTheme = [
'adunit-themes',
`adunit-themes--${type}`.toLowerCase(),
`adunit-themes--${theme}`.toLowerCase(),
`adunit-themes--${type}-${theme}`.toLowerCase(),
]
}
const renderOfferModuleWithoutDisplayAdContainersWithAboveTemplate =
ctx.props.data.Decorate?.Position === 'AboveAdunit' || false
const renderOfferModuleWithoutDisplayAdContainers =
ctx.props.data.Decorate?.RemoveAds /* for adunits */ ||
ctx.props.data.DeviceData?.RemoveAds /* for devices */ ||
false
const getStyle = (className) => {
try {
return ctx.parent.$style[className]
} catch (error) {
console.log('$test', 'invalid style not found on parent selector')
}
}
const PrimaryOfferModule = (aboveAdunitSlot = {}) =>
h(Component, {
props: {
data: ctx.props.data,
itemIndex: i,
adunitTheme: adunitTheme.join('.')
},
attrs: {
class: [
...adunitTheme,
getStyle('product')
]
.join(' ')
.trim()
},
scopedSlots: {
...aboveAdunitSlot
}
})
if (renderOfferModuleWithoutDisplayAdContainersWithAboveTemplate) {
return [
PrimaryOfferModule({
aboveAdunit (props) {
return h({
data () {
return ctx.props.data.Decorate
},
template: ctx.props.data.Decorate?.Template.replace(
'v-show="false"',
''
)
})
}
})
]
} else if (renderOfferModuleWithoutDisplayAdContainers) {
return [PrimaryOfferModule()]
} else {
const withAd = i > 0 && i % 1 === 0
const adWrap = (placement, position, className) => {
return h(
'div',
{
class: 'm4d-wrap-sticky'
},
[
h(Advertisement, {
props: {
placement,
position: String(position)
},
class: getStyle(className)
})
]
)
}
return [
withAd && adWrap('inline-sticky', i, 'inlineAd'),
h('div', {
class: 'm4d-wrap-sticky-adjacent'
}),
h(
'div',
{
attrs: {
id: `inline-device--${String(i)}`
},
class: 'inline-device'
},
isDev(`inline-device id#: inline-device--${String(i)}`)
),
withAd &&
i !== ctx.props.len - 1 &&
h(EcomAdvertisement, {
props: {
placement: 'inline-static',
position: String(i)
},
class: getStyle('inlineStaticAd')
}),
PrimaryOfferModule()
]
}
}
}

事实证明,返回VNode数组实际上早于scopedSlots更新。

我在文档中也找不到它的文档,但通过Vue.js核心团队的一名成员对Vue GitHub问题的评论(比scopedSlots提交早了大约1年(,render()可以返回一个VNode数组,Vue将按顺序获取并呈现该数组然而,这只适用于一种特殊情况:功能组件。

试图在正常(非功能、有状态(组件中返回一个元素大于1的VNode数组会导致错误:

Vue.config.productionTip = false;
Vue.config.devtools = false;
Vue.component('render-func-test', {
render(h, ctx) {
return [
h('h1', "I'm a heading"),
h('h2', "I'm a lesser heading"),
h('h3', "I'm an even lesser heading")
];
},
});
new Vue({
el: '#app',
});
<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
<div id="app">
Test
<render-func-test></render-func-test>
</div>

[Vue warn]: Multiple root nodes returned from render function. Render function should return a single root node.

但是在功能组件中执行此操作,就像您的示例一样,效果很好:

Vue.config.productionTip = false;
Vue.config.devtools = false;
Vue.component('render-func-test', {
functional: true, // <--- This is the key
render(h, ctx) {
return [
h('h1', "I'm a heading"),
h('h2', "I'm a lesser heading"),
h('h3', "I'm an even lesser heading")
];
},
});
new Vue({
el: '#app',
});
<script src="https://unpkg.com/vue@2/dist/vue.js"></script>
<div id="app">
Test
<render-func-test></render-func-test>
</div>


如果您对为什么感兴趣,Vue核心团队的另一名成员在线程中进一步解释了这一限制。

它基本上可以归结为Vue修补和困难算法所做的假设;每个子组件在其父虚拟DOM中由单个VNode"表示;,如果允许多个根节点,则这是不真实的。

为了实现这一点,复杂性的增加将需要对Vue的核心算法进行重大更改。这是一件大事,因为该算法不仅必须擅长它所做的事情,而且必须非常、非常的性能。

功能组件不需要符合这个限制,因为">它们在父级中不使用VNode表示,因为它们没有实例,也不管理自己的虚拟DOM"-它们是无状态的,这使得限制变得不必要。

然而,应该注意的是,这Vue 3中的非功能组件上是可能的,因为有问题的算法被重新设计以允许它

这似乎是在中实现的

https://github.com/vuejs/vue/commit/c7c13c2a156269d29fd9c9f8f6a3e53a2f2cac3d

这是2018年提出的一个问题的结果(https://github.com/vuejs/vue/issues/8056),因为这个$scopedSlots.default((根据内容返回了一个VNode或一组VNode。

主要论点是,这与常规插槽在渲染函数中的行为不一致,这意味着任何将作用域插槽渲染为子级的渲染函数组件都需要键入并检查调用插槽的结果,以决定是否需要将其封装在数组中

所以Evan在这里评论了这个问题,解释了这一点$scopedSlots.default将始终返回v2.6版本开始的Arrays以实现一致性,但为了避免破坏$scopedSlots使用方式的更改,更新还将允许从呈现函数返回单个VNode的Array。

最新更新