5

我想Card使用 vue 创建一个组件,该组件具有使用vuejs 插槽 api的标题和内容部分。

我无法理解如何在 Card 组件中创建结构。卡片应包含所有线框样式(填充和边距),但可以从使用该组件的子模板扩展。

我想知道是否有任何标准方法可以从子组件中为带有插槽的包装器元素设置样式。

基本Card元素有 2 个插槽,其中包含结构元素,我希望允许子模板在必要时进行修改。

<div class="Card">
  <header class="CardHeader">
    <slot name="header"></slot>
  </header>
  <div class="CardContent">
    <slot></slot>
  </div>
</div>

理想情况下,我想为元素添加一个:class属性,#slot并在插槽中的元素上使用该属性,但是,旧语法似乎在最新版本的框架中已被贬低,例如。

<Card>
  <template #header class="extendHeader">
    stuff
  </template>
</Card>

我可以想到 3 种可行的方法,每种方法都有其缺点。

  1. 使用 props 通过根元素添加一个类
  2. 在根元素上使用类并在组件元素上使用样式挂钩
  3. 将插槽内容包装在另一个元素中

我的项目必须使用 css-modules 来减少尽可能多的全局 css,因为它在过去给我带来了如此多的悲痛。

1.使用props通过根元素添加一个类

这种方法将允许我使用 css-modules 并使Card元素打开以进行扩展

组件模板需要从子模板中组合额外的样式

<header :class="$style.Header, ...headerClasses]">
  <slot name="header"></slot>
</header>

然后子模板可以:header-classes用来扩展基本的 Card 类,或覆盖不需要的样式。

<Card :header-classes="[$style.header]">
  <template #header>
    stuff
  </template>
</Card>
<style module>
  .header {
    background: var(--v-blue);
  }
</style>

2. 在根元素上使用类并在组件元素上使用样式挂钩

模板将需要分配额外的类,这些Card类可用于扩展组件的基本结构组件。不应使用 css-module 类,因为名称经过哈希处理以避免冲突。

<div :class="[$style.Card, 'CardStyleHook']">
  <header :class="[$style.Header, 'CardHeaderStyleHook']">
    <slot name="header"></slot>
  </header>
  <div :class="[$style.Content, 'CardContentStyleHook']">
    <slot></slot>
  </div>
</div>

然后可以将单个类添加到组件中,并且可以使用额外类的样式

<Card :class="$style.extendCard">
  <template #header>
    stuff
  </template>
</Card>
<style module>
  .extendCard .CardHeaderStyleHook {
    background: var(--v-blue);
  }
</style>

3. 将槽内容包裹在另一个元素中

我认为这种方式违背了插槽的目的,它会导致我需要从Card.

<Card>
  <template #header>
    <div :class="$style.customHeader">
      stuff
    </div>
  </template>
</Card>
<style module>
  .customHeader{
    background: var(--v-blue);
  }
</style>

由于旧插槽已折旧,是否有任何标准方法来处理这些情况?我是否错过了文档中的某些内容,我只玩了几天 vue,所以这绝对是可能的。

如果您需要提供任何示例,下面的代码片段中有完整的示例以及工作代码。

谢谢你的帮助!

const use$styleMocking = {
  beforeMount() {
    // mock css-modules output
    this.$style = {
      Card: 'Card__x1337',
      Header: 'CardHeader__x1337',
      Content: 'CardContent__x1337',
    }
  }
}

Vue.component('Card', {
  template: '#card-template',
  mixins: [ use$styleMocking ],
  props: {
    cardClasses: { default: () => [] },
    headerClasses: { default: () => [] },
    contentClasses: { default: () => [] },
  }
})

Vue.component('style-hooked-card', {
  template: '#style-hooked-card-template',
  mixins: [ use$styleMocking ]
})

const app = new Vue({
  el: '#app',
  template: '#app-template'
})
:root {
  --white: #fff;
  --v-teal: #00c58e;
  --v-jade: #108775;
  --v-blue: #2f495e;
  --card-padding: 0.5rem 1rem;
  --grid-gap: 1.25rem;
}

html, body {
  font-family: sans-serif;
  line-height: 1.44;
}

.main {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-column-gap: var(--grid-gap);
  grid-row-gap: var(--grid-gap);
}

/* --------- Card Styles --------- */
/*
  styles have __x1337 to mock css-modules, the client should never know the hash
  and should not use it to style elements
*/
.Card__x1337 {
  box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23);
  transition: 400ms box-shadow;
  margin: 0;
  flex: 1;
}
.CardHeader__x1337 {
  padding: var(--card-padding);
  background: var(--v-teal);
}
.CardHeader__x1337 h3 {
  color: var(--v-blue);
  margin: 0;
  padding: 0;
  font-size: 1rem;
}
.CardContent__x1337 {
  padding: var(--card-padding);
}
/* --------- End Card Styles --------- */

.extendHeader {
  background: var(--v-blue);
}

.extendHeader h3 {
  color: var(--white);
}

.extendCard .CardHeaderStyleHook {
  background: var(--v-jade);
}

.extendCard .CardHeaderStyleHook h3 {
  color: var(--white);
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<template type="text/x-template" id="app-template">
  <main class="main">
    <Card>
      <template #header class="extendHeader">
        <h3>Basic Card Styles</h3>
      </template>
      <template>
        <p>It would be ideal to add a class to the template and have it available to add to the classes of the wrapper element around slots</p>
      </template>
    </Card>
    
    <Card :header-classes="['extendHeader']">
      <template #header>
        <h3>1. Extend Header Styles</h3>
      </template>
      <template>
        <p>This card uses props on the Card to extend the wrapper class, this example is using a global class, but this would use css-modules locally scoped classes correctly</p>
      </template>
    </Card>
    
    <Style-Hooked-Card class="extendCard">
      <template #header>
        <h3>2. Extend Card Styles</h3>
      </template>
      <template>
        <p>This card adds a hook class to every component so that it can be extended from the outside</p>
      </template>
    </Style-Hooked-Card>
  </main>
</template>

<template type="text/x-template" id="card-template">
  <div :class="[$style.Card, ...cardClasses]">
    <header :class="[$style.Header, ...headerClasses]">
      <slot name="header"></slot>
    </header>
    <div :class="[$style.Content, ...contentClasses]">
      <slot></slot>
    </div>
  </div>
</template>

<template type="text/x-template" id="style-hooked-card-template">
  <div :class="[$style.Card, 'CardStyleHook']">
    <header :class="[$style.Header, 'CardHeaderStyleHook']">
      <slot name="header"></slot>
    </header>
    <div :class="[$style.Content, 'CardContentStyleHook']">
      <slot></slot>
    </div>
  </div>
</template>

<div id="app"></div>

4

0 回答 0