我的动态组件(布局)不适用于 vuejs 中的命名插槽

My dynamic component (layout) doesn't work with named slots in vuejs

我在将动态生成的布局与命名插槽组合时遇到问题。

为了定义我的布局,我使用“component :is”

//app.vue
<template>
  <component :is="layout">
   <router-view />
  </component>
</template>
<script>
  computed: {
    layout() {
      const layout = this.$route.meta.layout || 'default'
      return () => import(`@/app/layouts/${layout}.vue`)
    }
  },
</script>
//layouts/default.vue
<template>
  <div>
    <div>
      <slot name="header" />
    </div>
    <div>
      <div>
        <slot name="sidebar" />
       </div>
       <div>
         <slot name="default"/>
       </div>
    </div>
  </div>
</template>
// views/page.vue
<template>
  <div>
    <template #header>
      <h1>Primitives</h1>
    </template>
    <template #sidebar>
      <ul>
        <li v-for="primitive in sections.sections" :key="primitive">
          <router-link :to="`/primitives/${primitive}`">{{primitive}}</router-link>
        </li>
      </ul>
    </template>
    <template #default>
      <router-view :key="$router.path" />
    </template>
  </div>
</template>

但是现在我的代码中出现了这个错误

'v-slot' directive must be owned by a custom element, but 'div' is not.

并且控制台显示此错误

<\template v-slot> can only appear at the root level inside the receiving component

如果我删除主 div 我会得到错误

The template root requires exactly one element.

我做错了什么?

这个不好解释,请大家多多包涵...

我真的明白你想做什么,但不幸的是,这在 Vue 中是不可能的。

原因是插槽比 Vue 的 模板编译器 特性多于 运行时特性。我的意思是什么?当 Vue 模板编译器看到类似 <template #header> 的东西时,它会获取内部内容并将其编译成一个返回虚拟 DOM 元素的函数。此函数必须传递给某个组件,该组件可以调用它并将结果包含在它自己生成的虚拟 DOM 中。为此,模板编译器需要知道它应该将函数传递给哪个组件(这是 'v-slot' directive must be owned by a custom element, but 'div' is not. 错误消息的真正含义......即编译器正在“寻找”一个组件以将槽内容传递给...... .)

但是您正在尝试使用插槽,就好像它们在运行时是“可发现的”一样。为了让您的代码正常工作,动态布局组件必须 在运行时 以某种方式发现它的子组件(由于 <router-view /> 也是动态的)有一些可以使用的插槽内容。这不是插槽在 Vue 中的工作方式。您可以 但不要指望父组件(在本例中为布局)可以“发现”在其子组件中定义的插槽内容...

不幸的是,您的问题的唯一解决方案是在每个“页面”中导入布局组件并将其用作模板中的根元素。您可以使用 mixins 来减少代码重复(定义 layout computed)

@/mixins/withLayout.js

export default = {
  computed: {
    layout() {
      const layout = this.$route.meta.layout || 'default'
      return () => import(`@/app/layouts/${layout}.vue`)
    }
  }
}

views/page.vue

<template>
  <component :is="layout">
    <template #header>
      <h1>Primitives</h1>
    </template>
    <template #sidebar>
      <ul>
        <li v-for="primitive in sections.sections" :key="primitive">
          <router-link :to="`/primitives/${primitive}`">{{primitive}}</router-link>
        </li>
      </ul>
    </template>
    <template #default>
      <router-view :key="$router.path" />
    </template> 
  </component>
</template>
<script>
import withLayout from '@/mixins/withLayout'

export default {
  mixins: [withLayout]
}
</script>