使用 CSS 样式化 Vue 组件

终于到了让我们的应用看起来更漂亮的时候了。在本文中,我们将探讨使用 CSS 对 Vue 组件进行样式设置的不同方法。

先决条件

熟悉核心 HTMLCSSJavaScript 语言,了解 终端/命令行

Vue 组件以 JavaScript 对象的组合形式编写,这些对象管理应用程序的数据,以及一个基于 HTML 的模板语法,该语法映射到底层 DOM 结构。对于安装,以及使用 Vue 的一些更高级功能(如单文件组件或渲染函数),您需要一个安装了 node + npm 的终端。

目标 了解如何对 Vue 组件进行样式设置。

使用 CSS 样式化 Vue 组件

在我们继续为我们的应用添加更多高级功能之前,我们应该添加一些基本的 CSS 来让它看起来更好。Vue 有三种常见的应用程序样式方法

  • 外部 CSS 文件。
  • 单文件组件(.vue 文件)中的全局样式。
  • 单文件组件中的组件范围样式。

为了帮助您熟悉每个方法,我们将结合使用这三种方法来使我们的应用程序拥有更美观的外观和感觉。

使用外部 CSS 文件样式化

您可以包含外部 CSS 文件,并将其全局应用于您的应用程序。让我们看看如何做到这一点。

首先,在 src/assets 目录中创建一个名为 reset.css 的文件。此文件夹中的文件由 Webpack 处理。这意味着我们可以使用 CSS 预处理器(如 SCSS)或后处理器(如 PostCSS)。

虽然本教程不会使用这些工具,但您应该知道,在将此类代码包含在 assets 文件夹中时,它将自动处理。

将以下内容添加到 reset.css 文件中

css
/*reset.css*/
/* RESETS */
*,
*::before,
*::after {
  box-sizing: border-box;
}
*:focus {
  outline: 3px dashed #228bec;
}
html {
  font: 62.5% / 1.15 sans-serif;
}
h1,
h2 {
  margin-bottom: 0;
}
ul {
  list-style: none;
  padding: 0;
}
button {
  border: none;
  margin: 0;
  padding: 0;
  width: auto;
  overflow: visible;
  background: transparent;
  color: inherit;
  font: inherit;
  line-height: normal;
  -webkit-font-smoothing: inherit;
  -moz-osx-font-smoothing: inherit;
  appearance: none;
}
button::-moz-focus-inner {
  border: 0;
}
button,
input,
optgroup,
select,
textarea {
  font-family: inherit;
  font-size: 100%;
  line-height: 1.15;
  margin: 0;
}
button,
input {
  /* 1 */
  overflow: visible;
}
input[type="text"] {
  border-radius: 0;
}
body {
  width: 100%;
  max-width: 68rem;
  margin: 0 auto;
  font:
    1.6rem/1.25 "Helvetica Neue",
    Helvetica,
    Arial,
    sans-serif;
  background-color: #f5f5f5;
  color: #4d4d4d;
  -moz-osx-font-smoothing: grayscale;
  -webkit-font-smoothing: antialiased;
}
@media screen and (min-width: 620px) {
  body {
    font-size: 1.9rem;
    line-height: 1.31579;
  }
}
/*END RESETS*/

接下来,在 src/main.js 文件中,像这样导入 reset.css 文件

js
import "./assets/reset.css";

这将导致该文件在构建步骤中被拾取,并自动添加到我们的站点。

现在应将重置样式应用于应用程序。以下图片显示了应用程序在应用重置之前和之后的外观。

之前

the todo app with partial styling added; the app is now in a card, but some of the internal features still need styling

之后

the todo app with partial styling added; the app is now in a card, but some of the internal features still need styling

明显的变化包括删除列表项目符号、背景颜色更改以及基本按钮和输入样式的更改。

将全局样式添加到单文件组件

现在我们已经将 CSS 重置为在所有浏览器中保持一致,我们需要进一步自定义样式。我们希望在应用程序中的组件之间应用一些样式。虽然直接将这些文件添加到 reset.css 样式表中也能正常工作,但我们将改为将其添加到 App.vue 中的 <style> 标签中,以演示如何使用它。

该文件中已经有一些样式存在。让我们删除它们,并用以下样式替换它们。这些样式执行一些操作 - 为按钮和输入添加一些样式,以及自定义 #app 元素及其子元素。

更新您的 App.vue 文件的 <style> 元素,使其看起来像这样

html
<style>
  /* Global styles */
  .btn {
    padding: 0.8rem 1rem 0.7rem;
    border: 0.2rem solid #4d4d4d;
    cursor: pointer;
    text-transform: capitalize;
  }
  .btn__danger {
    color: #fff;
    background-color: #ca3c3c;
    border-color: #bd2130;
  }
  .btn__filter {
    border-color: lightgrey;
  }
  .btn__danger:focus {
    outline-color: #c82333;
  }
  .btn__primary {
    color: #fff;
    background-color: #000;
  }
  .btn-group {
    display: flex;
    justify-content: space-between;
  }
  .btn-group > * {
    flex: 1 1 auto;
  }
  .btn-group > * + * {
    margin-left: 0.8rem;
  }
  .label-wrapper {
    margin: 0;
    flex: 0 0 100%;
    text-align: center;
  }
  [class*="__lg"] {
    display: inline-block;
    width: 100%;
    font-size: 1.9rem;
  }
  [class*="__lg"]:not(:last-child) {
    margin-bottom: 1rem;
  }
  @media screen and (min-width: 620px) {
    [class*="__lg"] {
      font-size: 2.4rem;
    }
  }
  .visually-hidden {
    position: absolute;
    height: 1px;
    width: 1px;
    overflow: hidden;
    clip: rect(1px 1px 1px 1px);
    clip: rect(1px, 1px, 1px, 1px);
    clip-path: rect(1px, 1px, 1px, 1px);
    white-space: nowrap;
  }
  [class*="stack"] > * {
    margin-top: 0;
    margin-bottom: 0;
  }
  .stack-small > * + * {
    margin-top: 1.25rem;
  }
  .stack-large > * + * {
    margin-top: 2.5rem;
  }
  @media screen and (min-width: 550px) {
    .stack-small > * + * {
      margin-top: 1.4rem;
    }
    .stack-large > * + * {
      margin-top: 2.8rem;
    }
  }
  /* End global styles */
  #app {
    background: #fff;
    margin: 2rem 0 4rem 0;
    padding: 1rem;
    padding-top: 0;
    position: relative;
    box-shadow:
      0 2px 4px 0 rgb(0 0 0 / 20%),
      0 2.5rem 5rem 0 rgb(0 0 0 / 10%);
  }
  @media screen and (min-width: 550px) {
    #app {
      padding: 4rem;
    }
  }
  #app > * {
    max-width: 50rem;
    margin-left: auto;
    margin-right: auto;
  }
  #app > form {
    max-width: 100%;
  }
  #app h1 {
    display: block;
    min-width: 100%;
    width: 100%;
    text-align: center;
    margin: 0;
    margin-bottom: 1rem;
  }
</style>

如果检查应用程序,您会看到我们的待办事项列表现在在一个卡片中,并且我们对待办事项的格式进行了更好的格式化。现在我们可以继续编辑我们的组件,以使用其中的一些样式。

the todo app with partial styling added; the app is now in a card, but some of the internal features still need styling

在 Vue 中添加 CSS 类

我们应该将按钮 CSS 类应用于 ToDoForm 组件中的 <button>。由于 Vue 模板是有效的 HTML,因此与在普通 HTML 中进行操作的方式相同 - 通过向元素添加 class="" 属性。

class="btn btn__primary btn__lg" 添加到表单的 <button> 元素

html
<button type="submit" class="btn btn__primary btn__lg">Add</button>

趁我们在这里,我们还可以进行一项语义和样式更改。由于我们的表单表示页面中的特定部分,因此它可以从 <h2> 元素中受益。但是,标签已经表示了表单的目的。为了避免重复,让我们将标签包装在 <h2> 中。我们还可以添加一些其他全局 CSS 样式。我们还将 input__lg 类添加到 <input> 元素中。

更新您的 ToDoForm 模板,使其看起来像这样

html
<template>
  <form @submit.prevent="onSubmit">
    <h2 class="label-wrapper">
      <label for="new-todo-input" class="label__lg">
        What needs to be done?
      </label>
    </h2>
    <input
      type="text"
      id="new-todo-input"
      name="new-todo"
      autocomplete="off"
      v-model.lazy.trim="label"
      class="input__lg" />
    <button type="submit" class="btn btn__primary btn__lg">Add</button>
  </form>
</template>

让我们还将 stack-large 类添加到 App.vue 文件中的 <ul> 标签中。这将有助于稍微改善待办事项的间距。

按如下方式更新它

html
<ul aria-labelledby="list-summary" class="stack-large"></ul>

添加作用域样式

我们想要设置样式的最后一个组件是 ToDoItem 组件。为了使样式定义更靠近组件,我们可以在其中添加一个 <style> 元素。但是,如果这些样式改变了此组件之外的内容,则可能难以追踪导致样式问题的样式,并解决问题。这就是 scoped 属性可以派上用场的地方 - 它将唯一的 HTML data 属性选择器附加到您的所有样式,防止它们在全局范围内冲突。

要使用 scoped 修饰符,请在 ToDoItem.vue 中,在文件底部创建一个 <style> 元素,并赋予它一个 scoped 属性

html
<style scoped>
  /* … */
</style>

接下来,将以下 CSS 复制到新创建的 <style> 元素中

css
.custom-checkbox > .checkbox-label {
  font-family: Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-weight: 400;
  font-size: 16px;
  font-size: 1rem;
  line-height: 1.25;
  color: #0b0c0c;
  display: block;
  margin-bottom: 5px;
}
.custom-checkbox > .checkbox {
  font-family: Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-weight: 400;
  font-size: 16px;
  font-size: 1rem;
  line-height: 1.25;
  box-sizing: border-box;
  width: 100%;
  height: 40px;
  height: 2.5rem;
  margin-top: 0;
  padding: 5px;
  border: 2px solid #0b0c0c;
  border-radius: 0;
  appearance: none;
}
.custom-checkbox > input:focus {
  outline: 3px dashed #fd0;
  outline-offset: 0;
  box-shadow: inset 0 0 0 2px;
}
.custom-checkbox {
  font-family: Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  font-weight: 400;
  font-size: 1.6rem;
  line-height: 1.25;
  display: block;
  position: relative;
  min-height: 40px;
  margin-bottom: 10px;
  padding-left: 40px;
  clear: left;
}
.custom-checkbox > input[type="checkbox"] {
  -webkit-font-smoothing: antialiased;
  cursor: pointer;
  position: absolute;
  z-index: 1;
  top: -2px;
  left: -2px;
  width: 44px;
  height: 44px;
  margin: 0;
  opacity: 0;
}
.custom-checkbox > .checkbox-label {
  font-size: inherit;
  font-family: inherit;
  line-height: inherit;
  display: inline-block;
  margin-bottom: 0;
  padding: 8px 15px 5px;
  cursor: pointer;
  touch-action: manipulation;
}
.custom-checkbox > label::before {
  content: "";
  box-sizing: border-box;
  position: absolute;
  top: 0;
  left: 0;
  width: 40px;
  height: 40px;
  border: 2px solid currentcolor;
  background: transparent;
}
.custom-checkbox > input[type="checkbox"]:focus + label::before {
  border-width: 4px;
  outline: 3px dashed #228bec;
}
.custom-checkbox > label::after {
  box-sizing: content-box;
  content: "";
  position: absolute;
  top: 11px;
  left: 9px;
  width: 18px;
  height: 7px;
  transform: rotate(-45deg);
  border: solid;
  border-width: 0 0 5px 5px;
  border-top-color: transparent;
  opacity: 0;
  background: transparent;
}
.custom-checkbox > input[type="checkbox"]:checked + label::after {
  opacity: 1;
}
@media only screen and (min-width: 40rem) {
  label,
  input,
  .custom-checkbox {
    font-size: 19px;
    font-size: 1.9rem;
    line-height: 1.31579;
  }
}

现在我们需要向我们的模板添加一些 CSS 类来连接样式。

对于根 <div>,添加一个 custom-checkbox 类。对于 <input>,添加一个 checkbox 类。最后,对于 <label>,添加一个 checkbox-label 类。更新后的模板如下

html
<template>
  <div class="custom-checkbox">
    <input type="checkbox" :id="id" :checked="isDone" class="checkbox" />
    <label :for="id" class="checkbox-label">{{label}}</label>
  </div>
</template>

应用程序现在应该具有自定义复选框。您的应用程序应该看起来像下面的屏幕截图。

the todo app with complete styling. The input form is now styled properly, and the todo items now have spacing and custom checkboxes

总结

我们已经完成了示例应用程序的样式设置工作。在下一篇文章中,我们将回到为我们的应用程序添加更多功能,即使用计算属性将完成的待办事项项计数添加到我们的应用程序中。