【问题标题】:How to have attribute inheritance for Vue component slots?Vue 组件槽如何进行属性继承?
【发布时间】:2018-11-06 23:02:22
【问题描述】:

我正在编写一个 Vue 组件,它接受 2 个插槽:

<template>
  <div class="Component">
    <div class="Component-Label">
      <slot
        name="label"
        class="Component-LabelInner"
      />
    </div>
    <div class="Component-Input">
      <slot
        name="input"
        class="Component-InputInner"
      />
    </div>
  </div>
</template>


<style scoped>
.Component { ... }

.Component-Label { ... }

.Component-LabelInner { ... }

.Component-Input { ... }

.Component-InputInner { width: 100%; ... }
</style>

出于布局目的,我绝对需要对 &lt;slot&gt; 元素应用一些样式 - 具有 Component-LabelInnerComponent-InputInner 类的样式。

(准确地说,我需要将规则width: 100% 应用到Component-InputInner,因为通常我会传递到&lt;input&gt; 元素,并且我希望传递到那里的所有内容都延伸到容器。)

问题是&lt;slot&gt; 元素被提供给插槽的内容替换后,class 属性没有被继承(似乎没有在插槽上继承属性)和 .Component-LabelInner 和 @987654331 的 CSS 选择器@不工作。

你能以某种方式将 CSS 类添加到 &lt;slot&gt; 被替换的元素吗?

【问题讨论】:

  • &lt;div class="Component" 缺少&gt;
  • 真 ;)。已更正。
  • 您没有尝试将class="bla" 分配给&lt;slot&gt;,而是尝试将该类添加到包含元素引用中?另外,你确定你需要width: 100% - 我的意思是你确定它与display 属性值不是block 或类似的无关吗?
  • @RokoC.Buljan:我当然可以这样做,但这会破坏组件隔离的想法——我必须记住每次使用组件时都在组件外部添加该类。我正在寻找一个干净的解决方案,将所有内容都封装在组件中。
  • @RokoC.Buljan:我需要width: 100%,因为通常我会将&lt;input&gt; 传递给input 插槽。 &lt;input&gt;s 默认情况下没有 display: block,因此除非您明确告诉它们(我正在尝试这样做),否则它们不会调整大小以适应容器。

标签: javascript html css vue.js vue-component


【解决方案1】:

您不能将类绑定到slot 标记。有一些解决方案可以处理这种情况:

  1. 使用 Vue mounted 钩子(它有效,但看起来很糟糕):

Vue.component("slots-comp", {
  template: "#slotsCompTemplate",
  mounted() {
    // each slot is an array, because you can pass a set of nodes wrap them with template tag
    // I use only first VNode for example
    this.$slots.one && this.$slots.one[0].elm.classList.add("one");
    this.$slots.two && this.$slots.two[0].elm.classList.add("two");
  }
});

new Vue({
  el: "#app",
  data: {}
})
.one {
  color: red
}

.two {
  color: blue
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
   <slots-comp>
     <div slot="one">One</div>
     <div slot="two">Two</div>
   </slots-comp>
</div>

<script type="text/x-template" id="slotsCompTemplate">
  <div>
    <slot name="one"></slot>
    <slot name="two"></slot>
  </div>
</script>
  1. 将必要的类作为道具传递给作用域插槽(它不是完全封装的解决方案):

Vue.component("slots-comp", {
  template: "#slotsCompTemplate",
  data() {
  	return {
      classes: {
      	one: ["one"],
        two: ["two"]
      }
    }
  }
});

new Vue({
  el: "#app",
  data: {}
})
.one {
  color: red
}

.two {
  color: blue
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
   <slots-comp>
     <div slot-scope="props" :class="props.classes.one" slot="one">One</div>
     <div slot-scope="props" :class="props.classes.two" slot="two">Two</div>
   </slots-comp>
</div>

<script type="text/x-template" id="slotsCompTemplate">
  <div>
    <slot :classes="classes" name="one"></slot>
    <slot :classes="classes" name="two"></slot>
  </div>
</script>
  1. 添加对 CSS 的更改以将样式应用于所有内部元素:

    .Component-Input > * 
    {
        /* my rules for child elements */
    }
    
    .Component-Label> * 
    {
        /* my rules for child elements */
    }
    

    或者为具有Component-InputInner 等类的插槽添加包装器元素,并为它们添加类似的样式。

希望它会有所帮助。

【讨论】:

  • 谢谢,尤其是多种解决方案。我使用了解决方案#1(甚至昨天自己想出了)。正如您所说的#2 没有将解决方案封装在组件内(当您使用组件时,您必须记住在外部模板中应用属性)。 #3 将它保留在组件内,但我几乎完全避免使用通用选择器,因为它很容易泄漏样式。添加您在 #3 中提出的包装器将不起作用 - 我必须直接在插槽内的元素上应用样式。 #1 可能不是最优雅的,但在功能上没有任何缺点。
猜你喜欢
  • 2020-01-19
  • 2016-10-30
  • 2022-01-23
  • 2020-08-14
  • 1970-01-01
  • 2020-01-03
  • 2014-10-06
  • 2017-12-18
  • 1970-01-01
相关资源
最近更新 更多