我喜欢使用 Svelte 编写样式

Avatar of Ollie Williams
Ollie Williams

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费积分!

最近围绕 Svelte 的炒作非常多,而且该项目 在 GitHub 上获得了超过 24,000 个星标。Svelte 可以说是最简单的 JavaScript 框架,由 Rich Harris 开发,他是 Rollup 的开发人员。Svelte 有很多值得喜欢的地方(性能、内置状态管理、编写适当的标记而不是 JSX),但对我来说,最大的吸引力是它对 CSS 的方法。

单文件组件

​​

React 对样式的定义方式没有意见
—React 文档

​​​​

一个没有内置方法为组件添加样式的 UI 框架是不完整的。
—Rich Harris,Svelte 的创造者

在 Svelte 中,您可以像在典型项目中一样在样式表中编写 CSS。您也可以 使用 CSS-in-JS 解决方案,例如 styled-componentsEmotion,如果您愿意。将代码划分为组件而不是按文件类型进行划分变得越来越普遍。例如,React 允许将组件的标记和 JavaScript 放在一起。在 Svelte 中,这又向前迈进了一步:组件的 Javascript、标记和样式都可以放在一个 `.svelte` 文件中。如果您曾经在 Vue 中使用过单文件组件,那么 Svelte 看起来会很熟悉。

// button.svelte
<style>
  button {
    border-radius: 0;
    background-color: aqua;
  }
</style>

<button>
  <slot/>
</button>

样式默认情况下是作用域的

默认情况下,在 Svelte 文件中定义的样式是作用域的。与 CSS-in-JS 库或 CSS 模块类似,Svelte 在编译时生成唯一的类名,以确保一个元素的样式永远不会与另一个元素的样式冲突。

这意味着您可以在 Svelte 组件文件中使用简单的元素选择器(如 divbutton),而无需处理类名。如果我们回到前面示例中的按钮样式,我们知道 <button> 的规则集将只应用于我们的 <Button> 组件,而不会应用于页面中的任何其他 HTML 按钮元素。如果您在一个组件中有多个按钮,并且希望以不同的方式对其进行样式设置,您仍然需要使用类。类也将被 Svelte 作用域。

Svelte 生成的类看起来像是乱码,因为它们是根据组件样式的哈希值生成的(例如 svelte-433xyz)。这比 像 BEM 这样的命名约定容易得多。不过,不得不承认,在 DevTools 中查看样式的体验稍微差一点,因为类名缺乏意义。

DevTools 中 Svelte 组件的标记。

这不是非此即彼的情况。您可以将 Svelte 的作用域样式与常规样式表一起使用。我个人在 .svelte 文件中编写组件特定的样式,但会使用样式表中定义的实用程序类。对于要在整个应用程序中使用的全局样式——CSS 自定义属性、可重复使用的 CSS 动画、实用程序类、任何“重置”样式或像 Bootstrap 这样的 CSS 框架——我建议将它们放在与 HTML 文档头部链接的样式表中。

它让我们创建全局样式

正如我们刚刚看到的,您可以使用常规样式表来定义全局样式。如果您需要从 Svelte 组件中定义任何全局样式,也可以使用 :global 来实现。这本质上是在需要时选择退出作用域的一种方法。

例如,模态组件可能希望切换一个类来对 body 元素进行样式设置

<style>
:global(.noscroll) {
  overflow: hidden;
}
</style>

未使用的样式会被标记

Svelte 的另一个好处是,它会在编译期间提醒您任何未使用的样式。换句话说,它会搜索定义但从未在标记中使用的样式。

条件类简明扼要,毫不费力

如果 JavaScript 变量名和类名相同,语法就会非常简明。在这个示例中,我正在为全宽按钮和幽灵按钮创建修饰符属性。

<script>
  export let big = false;
  export let ghost = false;
</script>

<style>
  .big {
    font-size: 20px;
    display: block;
    width: 100%;
  }
  
  .ghost {
    background-color: transparent;
    border: solid currentColor 2px;
  }
</style>    
    
<button class:big class:ghost>
  <slot/>
</button>

当使用 ghost 属性时,元素将应用一个 ghost 类,当使用 big 属性时,元素将应用一个 big 类。

<script>
  import Button from './Button.svelte';
</script>

<Button big ghost>Click Me</Button>

Svelte 不要求类名和属性名相同。

<script>
  export let primary = false;
  export let secondary = false;
</script>

<button
  class:c-btn--primary={primary}
  class:c-btn--secondary={secondary}
  class="c-btn">
  <slot></slot>
</button>

上面的按钮组件将始终具有 c-btn 类,但只有在传递相关属性时才会包含修饰符类,如下所示

<Button primary>Click Me</Button>

这将生成以下标记

<button class="c-btn c-btn--primary">Click Me</button>

可以使用单个属性将任意数量的类传递给组件

<script>
let class_name = '';
export { class_name as class };
</script>

<button class="c-btn {class_name}">
  <slot />
</button>

然后,可以使用与使用 HTML 标记相同的方式使用类

<Button class="mt40">Click Me</Button>

从 BEM 到 Svelte

让我们看看与标准 CSS 命名约定相比,Svelte 使编写样式变得容易多少。这是一个使用 BEM 编写的简单组件。

.c-card {
  border-radius: 3px;
  border: solid 2px;
}

.c-card__title {
  text-transform: uppercase;
}

.c-card__text {
  color: gray;
}

.c-card--featured {
  border-color: gold;
}

使用 BEM,类会变得又长又难看。在 Svelte 中,事情变得简单多了。

<style>
div {
  border-radius: 3px;
  border: solid 2px;
}

h2 {
  text-transform: uppercase;
}

p {
  color: gray;
}

.featured {
  border-color: gold;
}
</style>

<div class:featured>
  <h2>{title}</h2>
  <p>
    <slot />
  </p>
</div>

它与预处理器配合得很好

在使用 Svelte 时,CSS 预处理器感觉不那么必要了,但它们可以通过使用名为 Svelte Preprocess 的包完美地协同工作。支持 Less、Stylus 和 PostCSS,但这里我们将介绍 Sass。我们首先需要安装一些依赖项

npm install -D svelte-preprocess node-sass

然后我们需要在文件的顶部在 rollup.config.js 中导入 autoPreprocess。

import autoPreprocess from 'svelte-preprocess';

接下来,让我们找到 plugins 数组,并将 preprocess: autoPreprocess() 添加到 Svelte 中

export default {
  plugins: [
    svelte({
      preprocess: autoPreprocess(),
      ...other stuff

然后,我们只需要在组件文件中工作时指定我们使用的是 Sass,使用 type="text/scss"lang="scss" 来设置样式标签。

<style type="text/scss">
  $pink: rgb(200, 0, 220);
  p {
    color: black;
    span {
      color: $pink;
    }
  }
</style>

无需运行时的动态值

我们已经看到,Svelte 附带了大多数 CSS-in-JS 的优势——但没有外部依赖!但是,第三方库可以做到 Svelte 做不到的一件事:在 CSS 中使用 JavaScript 变量。

以下代码无效,将无法工作:

<script>
  export let cols = 4;
</script>

<style>
  ul {
    display: grid;
    width: 100%;
    grid-column-gap: 16px;
    grid-row-gap: 16px;
    grid-template-columns: repeat({cols}, 1fr);
  }
</style>

<ul>
  <slot />
</ul>

但是,我们可以通过使用 CSS 变量来实现类似的功能。

<script>
  export let cols = 4;
</script>

<style>
  ul {
    display: grid;
    width: 100%;
    grid-column-gap: 16px;
    grid-row-gap: 16px;
    grid-template-columns: repeat(var(--columns), 1fr);
  }
</style>

<ul style="--columns:{cols}">
  <slot />
</ul>

多年来,我以各种不同的方式编写 CSS:Sass、Shadow DOM、CSS-in-JS、BEM、原子 CSS 和 PostCSS。Svelte 提供了最直观、易于使用和友好的样式 API。如果您想阅读更多关于此主题的内容,请查看 Rich Harris 撰写的标题恰如其分的文章 The Zen of Just Writing CSS