同一组件的多个实例,一个具有独特的样式?

Multiple instances of same component, one with unique style?

我正在构建一个包含三个卡片组件的页面。三张牌中的一张(中间一张)的样式与另外两张不同,我无法做到这一点。这是我目前所拥有的...

Card.svelte(下)

<script>
  export let subLevel;
  export let price;
  export let users;
  export let storage;
  export let sendLimit;
  import Button from "./Button.svelte";
</script>

<div class="card">
  <h2>{subLevel}</h2>
  <ul>
    <li class="price"><span class="dollar">$</span>{price}</li>
    <li class="spec">{storage} Storage</li>
    <li class="spec">{users} Users Allowed</li>
    <li class="spec">Send up to {sendLimit} GB</li>
  </ul>
  <Button>Learn more</Button>
</div>

<style>
  .card {
    display: flex;
    flex-flow: column nowrap;
    align-items: center;
    padding-top: 2rem;
    background-color: #fff;
    border-radius: 10px;
    max-width: 350px;
    color: var(--gray-blue);
    padding-inline: 29px;
  }
  .dollar {
    font-size: 2.5rem;
    margin-right: 0.5rem;
  }
  .price {
    display: flex;
    font-size: 4.5rem;
    align-items: center;
    justify-content: center;
    border-top: none;
    color: var(--dark-gray-blue);
  }
  ul {
    width: 100%;
    list-style: none;
    text-align: center;
  }
  li {
    text-decoration: none;
    border-top: 1px solid var(--gray-blue);
    padding-block: 13px;
  }
  li:last-child {
    border-bottom: 1px solid var(--gray-blue);
  }
</style>

Button.svelte(下)

<button><slot /></button>

<style>
  button {
    text-transform: uppercase;
    width: 100%;
    background: linear-gradient(135deg, #a2a7f0, #696edd);
    color: #fff;
    font-size: 0.8125rem;
    letter-spacing: 1.39px;
    padding-block: 0.875rem;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    margin-block: 2rem;
  }
</style>

App.svelte(下)

<script>
  import Card from "./components/Card.svelte";
  import Footer from "./components/Footer.svelte";

  // Card details
  const basicMonthly = {
    subLevel: "Basic",
    price: 19.99,
    storage: "500 GB",
    users: 2,
    sendLimit: 3,
  };
  const proMonthly = {
    subLevel: "Professional",
    price: 24.99,
    storage: "1 TB",
    users: 5,
    sendLimit: 10,
  };
  const masterMonthly = {
    subLevel: "Master",
    price: 39.99,
    storage: "2 TB",
    users: 10,
    sendLimit: 20,
  };
</script>

<main class="site-content">
  <Card {...basicMonthly} />
  <Card {...proMonthly} />
  <Card {...masterMonthly} />
</main>
<Footer />

<style>
</style>

我的问题是:如何向第二个 Card 组件添加独特的样式? 它应该具有以下样式:

中间组件的样式(下)

<style>
  .card {
    display: flex;
    flex-flow: column nowrap;
    align-items: center;
    padding-top: 2rem;
    background-color: linear-gradient(135deg, #a2a7f0, #696edd);
    border-radius: 10px;
    max-width: 350px;
    color: #fff;
    padding-inline: 29px;
  }
  .dollar {
    font-size: 2.5rem;
    margin-right: 0.5rem;
  }
  .price {
    display: flex;
    font-size: 4.5rem;
    align-items: center;
    justify-content: center;
    border-top: none;
    color: #fff;
  }
  ul {
    width: 100%;
    list-style: none;
    text-align: center;
  }
  li {
    text-decoration: none;
    border-top: 1px solid #fff;
    padding-block: 13px;
  }
  li:last-child {
    border-bottom: 1px solid #fff;
  }
</style>

... 和 Button.svelte 也必须更改中间组件。它的样式应如下所示:

<style>
  button {
    text-transform: uppercase;
    width: 100%;
    background: #fff;
    color: #6d72de;
    font-size: 0.8125rem;
    letter-spacing: 1.39px;
    padding-block: 0.875rem;
    border: none;
    border-radius: 6px;
    cursor: pointer;
    margin-block: 2rem;
  }
</style>

我是 Svelte 的新手,完全被这个难住了。我试过使用 $$restProps 和动态样式,但我似乎无法深入了解我需要的按钮和卡片 div 中嵌套的各种元素。

Svelte 可以做到这一点吗,还是我应该简单地构建另一个组件来满足我的需要?当然还有另一种(更干燥)的方法来做到这一点?非常感谢任何帮助!

有很多不同的方法可以解决这个问题,但我会做如下事情:

  1. 在 Card 和 Button 组件中引入一个名为“secondary”的布尔属性。这将默认为 false,但对于中间卡将设置为 true。控制我们是否应用替代样式。 Card 会将其传递给 Button 组件,因此如果 secondary = true 对于 Card,它对于 Button 也等于 true。

    export let secondary = false;
    
    <!-- App.svelte -->
    <main class="site-content">
      <Card {...basicMonthly} />
      <Card secondary {...proMonthly} />
      <Card {...masterMonthly} />
    </main>
    
    <!-- Card.svelte -->
    <!-- ... rest of template -->
    <Button {secondary}>Learn more</Button>
    

    如果您最终有多个不同的变体,将其改为字符串可能是有意义的,例如variant="secondary"variant="tertiary"

  2. 根据次要道具是否为真,将class“次要”添加到卡片和按钮。 Svelte 的 class directive 让这一切变得简单。

    <!-- Card.svelte -->
    <div class="card" class:secondary>
    
    <!-- Button.svelte -->
    <button class:secondary><slot /></button>
    
  3. 应用以下样式。虽然您为第二张卡片粘贴了许多不同的样式,但唯一发生变化的是背景和文本颜色。我将这些值移至 CSS custom properties。当应用 secondary class 时,自定义属性会更新,其他所有内容都使用新颜色。

    <!-- Card.svelte styles -->
    <style>
      .card {
        --background: #fff;
        --text-color: var(--gray-blue);
    
        display: flex;
        flex-flow: column nowrap;
        align-items: center;
        padding-top: 2rem;
        background: var(--background);
        border-radius: 10px;
        max-width: 350px;
        color: var(--text-color);
        padding-inline: 29px;
      }
    
      .card.secondary {
        --background: linear-gradient(135deg, #a2a7f0, #696edd);
        --text-color: #fff;
      }
    
      .dollar {
        font-size: 2.5rem;
        margin-right: 0.5rem;
      }
    
      .price {
        display: flex;
        font-size: 4.5rem;
        align-items: center;
        justify-content: center;
        border-top: none;
        color: var(--text-color);
      }
    
      ul {
        width: 100%;
        list-style: none;
        text-align: center;
      }
    
      li {
        text-decoration: none;
        border-top: 1px solid var(--text-color);
        padding-block: 13px;
      }
    
      li:last-child {
        border-bottom: 1px solid var(--text-color);
      }
    </style>
    
    <!-- Button.svelte styles -->
    <style>
      button {
        --background: linear-gradient(135deg, #a2a7f0, #696edd);
        --text-color: #fff;
    
        text-transform: uppercase;
        width: 100%;
        background: var(--background);
        color: var(--text-color);
        font-size: 0.8125rem;
        letter-spacing: 1.39px;
        padding-block: 0.875rem;
        border: none;
        border-radius: 6px;
        cursor: pointer;
        margin-block: 2rem;
      }
    
      button.secondary {
        --background: #fff;
        --text-color: #6d72de;
      }
    </style>
    

有了这些更新,就没有重复,您也不必创建单独的组件。您可以在 Svelte REPL.

中看到最终演示