微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决Vue.js 渲染函数是否允许返回 VNode 数组?

我正在扩展 Vue.js 前端应用程序。我目前正在检查功能组件中的渲染功能。在查看文档后,我目前的理解是功能组件中的渲染函数将返回一个使用 CreateElement aka h 创建的单个 VNode。 当我看到 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 实际上早于 the scopedSlots update

我也没有在文档中的任何地方找到它的记录,但是通过 this comment 上的 a Vue GitHub issue 由 Vue.js 核心团队的成员(早于 scopedSlots ~1 年),render() 可以 返回一个 VNode 数组,Vue 将按顺序获取和渲染。 然而,这只适用于一种单一的情况:functional components

尝试在正常(非功能性、有状态)组件中返回包含大于 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,});
<script src="https://unpkg.com/vue@2/dist/vue.js"></script>

<div id="app">
  Test
  <render-func-test></render-func-test>
</div>


如果您对为什么感兴趣,Vue 核心团队的另一位成员解释了这个限制further down in the thread

它基本上归结为 Vue 修补和差异算法所做的假设,主要是“每个子组件在其父虚拟 DOM 中由单个 VNode 表示”,如果允许多个根节点,则这是不正确的.

复杂性的增加将需要对该算法进行大量更改,这是 Vue 的核心。这是一个大问题,因为这个算法不仅必须擅长它的工作,而且必须非常、非常高性能。

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

然而,应该注意的是,this is possible on non-functional components in Vue 3,因为所讨论的算法经过重新设计以允许它。

,

这似乎是在:

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

这是 2018 年提出的问题 (https://github.com/vuejs/vue/issues/8056) 的结果,因为 this.$scopedSlots.default() 返回了 VNode 或 VNode 数组,具体取决于内容。

主要论点是这与渲染函数中常规插槽的行为方式不一致,这意味着任何渲染函数组件渲染作用域插槽,因为子项需要键入检查调用插槽的结果以确定是否需要将其包装一个数组

因此 Evan 对问题线程 here 发表评论,解释说 this.$scopedSlots.default 将始终返回 v2.6 开始的数组以实现一致性,但为了避免对 $scopedSlots 的使用方式进行破坏性更改,更新还允许从渲染函数返回单个 VNode 的数组。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。