如何在vue3渲染函数中检查插槽或vnode类型?

n6lpvg4x  于 2022-12-14  发布在  Vue.js
关注(0)|答案(1)|浏览(215)

我有一个vue TabContiainer组件,可以这样使用它:

<TabContainer v-model="activeTab">
    <TabHeader tab-id="1"> tab1 </TabHeader>
    <TabHeader tab-id="2"> tab2 </TabHeader>
    <TabHeader tab-id="3"> tab3 </TabHeader>
    <TabContent tab-id="1"> content 1 </TabContent>
    <TabContent tab-id="2"> content 2 </TabContent>
    <TabContent tab-id="3"> content 3 </TabContent>
</TabContainer>

好好干。

这是TabContainer代码:

import { h } from 'vue'
// import './TabContainer.scss' //ignore css

const TabContainer = {
  name: 'TabContainer',
  props: {
    modelValue: {
      type: String,
      required: true,
    },
  },
  render() {
    const slots = this.$slots.default()
    console.log(slots)
    // check slot type
    const existInValidSubCom = slots.some(slot => ![TabHeader, TabContent].includes(slot.type))
    if (existInValidSubCom) {
      const message = "TabContainer's sub commpont muse be  TabHeader and TabContent"
      // throw new Error(message)
      return h('div', message)
    }
    const Tabs = slots
      .filter(item => item.type === TabHeader)
      .map(Tab =>
        h(Tab, {
          class: {
            tab: true,
            active: Tab.props['tab-id'] === this.modelValue,
          },
          onClick: () => {
            this.$emit('update:modelValue', Tab.props['tab-id'])
          },
        }),
      )
    const content = slots.find(
      slot => slot.type === TabContent && slot.props['tab-id'] === this.modelValue,
    )
    return [h('div', { class: 'tab-container' }, Tabs), h('div', content)]
  },
}

export default TabContainer
export const TabHeader = TabItem({ name: 'TabHeader' })
export const TabContent = TabItem({ name: 'TabContent' })

function TabItem(options) {
  return {
    ...options,
    props: {
      tabId: {
        type: String,
        required: true,
      },
    },
    render() {
      return h('div', null, this.$slots.default())
    },
  }
}

如果我像这样在插槽中添加一些注解,它将不起作用,

<TabContainer v-model="activeTab">
    <TabHeader tab-id="1"> tab1 </TabHeader>
    <TabHeader tab-id="2"> tab2 </TabHeader>
    <TabHeader tab-id="3"> tab3 </TabHeader>
    <!-- some comment -->
    <TabContent tab-id="1"> content 1 </TabContent>
    <TabContent tab-id="2"> content 2 </TabContent>
    <TabContent tab-id="3"> content 3 </TabContent>
</TabContainer>

一些注解导致以下代码在TabContainer中为真

// check slot type
const existInValidSubCom = slots.some(slot => ![TabHeader, TabContent].includes(slot.type))
  if (existInValidSubCom) {
    const message = "TabContainer's sub commpont muse be  TabHeader and TabContent"
    // throw new Error(message)
   return h('div', message)
}

为什么需要检查子组件类型?与table tag一样,table中允许使用有限的tag,只能使用head tbody tr td。希望vue组件之间有这样的限制或类似的功能。

控制台插槽:

存在一个符号(注解)。
它不会呈现为except。如果我使用v-for,也会出现同样的问题。

<TabContainer v-model="activeTab">
    <TabHeader :tab-id="item" v-for="(item, index) in ['1', '2', '3']" :key="index">
      tab{{ item }}
    </TabHeader>
    <TabContent tab-id="1"> content 1 </TabContent>
    <TabContent tab-id="2"> content 2 </TabContent>
    <TabContent tab-id="3"> content 3 </TabContent>
</TabContainer>

插槽具有符号(片段)vnode。

我试图找到一个好的方法来检查vnode或插槽类型。但没有得到一个好的。有什么更好的实现在我的情况下?如何可以得到它修复?

r1zhe5dt

r1zhe5dt1#

使用这些代码检查类型。

const vnodeList = this.$slots.default()
const childList = []
let existNonValidSubCom = false
let i = 0
do {
  const vnode = vnodeList[i]
  // html tag
  if (typeof vnode.type === 'string') {
    existNonValidSubCom = true
    break
  } else if ([TabHeader, TabContent].includes(vnode.type)) {
    childList.push(vnode)
  }
  else if (typeof vnode.type === 'symbol' && Array.isArray(vnode.children)) {
    // Symbol(Fragment)
    // childList.push(h(vnode.type, null, vnode.children))
    vnode.children.forEach(child => {
      if ([TabHeader, TabContent].includes(child.type)) {
        childList.push(child)
      }
    })
  }
  else if (typeof vnode.type === 'symbol' && typeof vnode.children === 'string') {
    // Symbol(Comment) skip
  } else {
    existNonValidSubCom = true
    break
  }
} while (++i < vnodeList.length && !existNonValidSubCom)

相关问题