将插槽传递给插槽

Posted

技术标签:

【中文标题】将插槽传递给插槽【英文标题】:Passing slot to slot 【发布时间】:2020-03-12 08:34:57 【问题描述】:

我正在构建一个小型库,它允许从其他组件中修改应用程序布局的一部分。

基本思想是拥有一个主导航组件:

<template>
  <div class='main-navigation>
    <div class='logo'><img...>
    <div class='actions'>
      <Container name='extra-actions' />
      <a href="some-action">Action</a>
    </div>
</template>

然后组件可以注册额外的内容:

<template>
  <div class='home-page'>
    <ContentFor container="extra-actions">
      <a href='some-home-specific-action'>Do sth extra</a>
    </ContentFor>

    ...rest of the page
  </div>
</template>

我已经设法使上述工作正常工作,使用自定义插件和服务对象将插槽注册(和更新)为 ContentFor 中定义的 VNode 并将其呈现在容器中。我现在想通过允许用户根据给定的内容添加自定义布局来增强它,例如:

<ul class='actions'>
  <Container name='extra-actions'>
    <li><slot></slot></li>
  </Container>
</ul>

这可以很好地将视图组件与导航结构分离。我尝试了以下方法:

render (h) 
  return h(Fragment, Object.values(this.contents).map((content) => 
    if (this.$scopedSlots.default) 
      return this.$scopedSlots.default(
        slots:  default: content.slot  # this does nothing!
      )
     else 
      # This works as expected
      return content.slot
    
  ))
,

当没有自定义模板时,上面的工作就很好。当自定义模板存在时,它会渲染该模板,但不会将内容传递到模板的插槽,从而导致:

<ul class='actions'>
  <li></li> # instead of <li><a href='some-home-specific-action'>Do sth extra</a></li>
</ul>

是否有任何特定的方式可以将作用域传递给其他作用域?

【问题讨论】:

我怀疑一个插槽可以有另一个插槽作为直接子级。您是否尝试过使用模板而不是渲染函数? @IVOGELOV - 渲染函数比模板(无论如何编译到渲染函数上)更加健壮。问题似乎是因为&lt;slot&gt; 在父上下文中使用,它填充了导航标签的内容。我同意插槽在这里不起作用,但问题是:什么会? 渲染函数是命令式的,而模板是声明式的——声明式总是比命令式代码更容易理解。也许如果您尝试仅使用模板来构建您的意图 - 它会帮助您将解决方案转换为渲染函数 ... @IVOGELOV - 这里的问题是我正在循环一组 VNode。没有办法直接使用模板语法来使用它们(需要为此创建一个自定义功能组件),所以我在某处使用渲染功能。最后,无论如何我都必须创建这样一个组件,并且我通过 scopedSlots 将 VNodes 传递到模板槽(必须将这些 vNodes 传递给该组件)。它丑得要命,但似乎没有更干净的出路。 我明白了。不过,在我看来,你把事情复杂化了。无论如何,从我的角度来看,如果您有 2 个组件 - 组件 1 为 &lt;ul&gt;&lt;slot/&gt;&lt;/ul&gt; 和组件 2 为 &lt;li&gt;&lt;slot/&gt;&lt;/li&gt; 然后您想将它们组合为 &lt;component1&gt;&lt;component2&gt;text&lt;/component2&gt;&lt;component2&gt;other&lt;/component2&gt;&lt;/component1&gt; 显然您首先创建组件 2 实例,之后 - 组件 1,并且仅在最后 - 呈现上述序列的***包装器。 【参考方案1】:

所以在反复讨论之后,我了解到这是不可能用插槽来解决我的模板问题的。当我这样做时

# layout/Navigation.vue
<ul>
  <Container name='main-nav-actions>
    <li><slot/></li>
  </Container>
</ul>

&lt;/slot&gt; 在 Navigation 组件的上下文中被解析 - 这意味着此时它是 VNode 的静态数组。因此,插槽不能有自己的动态嵌套插槽。

相反,我必须编写一个负责渲染 VNode 的功能组件:

export 
  name: 'RenderContent',
  functional: true,
  render (h,  props )  return props.content 

暴露后,我现在可以使用 scopedSlot 构建我的模板:

  <Container name='main-nav-actions vue-slot=" content ">
    <li><RenderContent :content="content"></li>
  </Container>

这不是最漂亮的解决方案,但它似乎有效,允许通过 ContentFor 传递可选选项,这非常棒。

【讨论】:

以上是关于将插槽传递给插槽的主要内容,如果未能解决你的问题,请参考以下文章

将父母道具传递给Vue,js中的插槽

Vue - 将插槽传递给子组件[重复]

将动态插槽从父级传递到子级到孙子级

如何在 VueJS 中将模板(插槽)从祖父组件传递给孙子?

将图像源传递到 Vue.js 中的作用域插槽

Vue.js 3 - 将组件插入插槽