受够了重复繁琐的css?来试试原子类吧

·  阅读 788

前言

在使用css的过程中,遇到了如下的痛点:

  1. 给类取名比较困难,刚开始语义化类名,后面逐渐松懈,导致很多无意义类名出现
  2. 使用预处理器时,嵌套的特别深
  3. 修改已有代码时,写在最后面或者直接加入行内样式,同一个组件的样式分开在不同的地方
  4. 类名越写越多,一个类名带有的样式越来越杂
  5. 颜色和字体属性不统一,导致页面一致性越来越差
  6. css和html脱节,一边翻css一边翻html
  7. 重复代码过多,有些属性经常出现在许多地方

思考过程

在前端开发中,可能大部分人对于css的使用都经历过这样几步 原生css->使用预处理器->自行封装工具类

注意:这里就不讨论ui框架了,ui框架定制性比较高,只探讨css的使用

这是一个层次递进的过程,就本人而言,因为原生css需要多次写同样的类名而使用预处理器。 如下的情况:

.parent {
    ...
}
.parent .parent-child-one{
    ...
}
.parent .parent-child-two{
    ...
}
...
复制代码

使用预处理器的嵌套则可以规避这种情况,但是预处理的嵌套可能被过分使用,造成深层次的嵌套,影响整体可读性,如下代码:

.history-map {
  .left {
    .time {
      .time-year {
      }
    }
    .line {
    }
  }
  .right {
    .title-map {
    }
    .context {
    }
  }
}
复制代码

过于重复的代码也会导致多次嵌套的发生,这个时候开始抽出一些公共的样式,作为工具类。如下是对flex工具的封装。

.utils-flex {
    display: flex;
}
.utils-flex-dir:extend(.utils-flex) {
    flex-direction: column;
}
.utils-flex-Justbetween:extend(.utils-flex) {
    justify-content: space-between;
}
.utils-flex-Justaround:extend(.utils-flex) {
    justify-content: space-around;
}
...
复制代码

我在使用封装的flex工具时,得到了非常好的体验,至少在flex布局上面,在再也不需要一面写html,一面过去写css,并且减少了许多重复的display:flex.使用如下:

<div class="history-map utils-flex-Justbetween">
  <div class="left utils-flex-dir utils-flex-Justbetween utils-flex-Aligncenter">
    <div class="time"><span class="time-year">{{dataMap.year}}</span></div>
    <div class="line"></div>
  </div>
  <div class="right utils-flex-dir utils-flex-Justbetween">
    <div class="title-map">{{dataMap.title}}</div>
    <div class="context">{{dataMap.context}}</div>
  </div>
</div>
复制代码

除了以上的优化手段,还可以使用预处理器带来的变量、函数、if/for等操作,但是css的编写不像js有一个明确的业务条件和边界条件,很多封装并不具有通用性,如果细化封装粒度的话,css属性实在是太多,有没有类似的工具库呢,有的,让我们接着往下看。

Tailwind CSS

Tailwind CSS 是一个功能类优先的 CSS 框架,它集成了诸如 flexpt-4text-center 和 rotate-90 这样的的类,它们能直接在脚本标记语言中组合起来,构建出任何设计。

以上是官网的介绍,简单的来说,就是这个工具把css的操作符和它对应的各种属性封装成了对应的类名,我们可以直接在html中,以使用类名的方式,直接构建页面。

image.png 至于它的安装与配置,这里就不细说了,可以去官网查看,也是非常的简单。
安装 - Tailwind CSS 中文文档

Tip
这是中文的官网,而中文官网只更新到2.2版本,最新版已经到了3.0.24,而3版本比起2版本有很大的变化,如果你不使用3版本,看中文文档即可。当然,安装还是一致的,大部分属性类名也没有变化。

它的使用方式非常简单,在根据官方说明安装后,我们需要下载对应的插件,以提供类名提示。

  • vscode: 在插件商店中下载Tailwind CSS IntelliSense插件以获得支持
  • webstorm: 在插件商店下载Tailwind CSSTailwind CSS Smart Completions,其中第一个插件用来支持tailwind css,第二个工具用来支持在预处理器中使用tailwind css以及使用它提供的@apply函数。

Tip
webstorm 社区版,无法使用该插件,版本过低也无法使用,2021版本对于tailwind 3.0没有类名提示,只有升级到2022。

<bl-icon
  name="close"
  class="text-gray-400 hover:text-gray-600 hover:border-gray-600 transition cursor-pointer border rounded-full"
  @click="clearModel"
/>
复制代码

比如这个例子,一个vue图标组件。在这上面的属性简单介绍:

  1. text-gray-400: 字体颜色 灰色 灰度400
  2. hover:text-gray-600: hover 鼠标滑上后 字体颜色灰度增加
  3. transition: 缓动
  4. border: 基本边框

...

这些类名大部分是跟着css属性的名字来的,但是也有一些不一样,需要一点点的记忆成本,不过上手非常快。 有人可能觉得这样写,会导致html变的非常长,不好看,twilwind css还提供了@apply函数,可以把这些属性聚合起来,可以简单理解为js的扩展运算符,把这些类名代表的属性拿出去放到一个类名中,比如这样使用:

image.png 这个bl-drawer类名,就拥有了这些类名所带有的属性,相当于把html上的一堆操作拿下来。当然在这个类名里面也可以正常的编写其他普通的css.

tailwind 一些独特功能

特殊状态

tailwind 除了支持普通的样式之外,还支持使用伪类比如要把hover状态下的背景颜色改为红色只需要这么写

<div class="hover:bg-red"></div>
复制代码

为了防止代码体积过大,有一些是默认开启的,比如hover,有一些就需要配置开启。 具体介绍请看文档: 悬停、焦点和其它状态 - Tailwind CSS 中文文档

响应式

上面那种hover:在tailwind中被称作变体,tailwind还拥有响应式变体,这种变体以移动优先。

image.png 要添加一个仅在特定断点生效的功能类,只需要在该功能类前加上断点名称,后面跟 : 字符。

<img class="w-16 md:w-32 lg:w-48" src="...">
复制代码

添加自己的样式

如你理解的那样,无论原子类粒度多么细,总有它无法满足的样式,所以在jit没有引入之前,tailwind支持通过配置添加自己常用的属性。比如我需要添加自己的颜色

const colors = require('tailwindcss/colors')
module.exports = {
  theme: {
    colors: {
      ...colors,
      primary: '#409EFF',
      success: '#67C23A',
      warning: '#E6A23C',
      danger: '#F56C6C',
      info: '#909399',
      transparent: 'transparent'
    },
  },
}
复制代码

这是tailwindcss生成的配置,我在颜色配置里面,添加了element ui 的几种颜色。这样,我就可以在我的代码中这样使用:

<div class="bg-primary text-success border-danger" />
复制代码

设置的颜色会自动注入到tailwind中所有运用了颜色的属性中,以便你使用它。更多的操作请自行查阅文档。

优点与缺点

优点:

  1. 减少了不必要的类名,不需要为取名字烦恼
  2. 快速构建,将样式和html联系在一起
  3. 减少了重复的样式,减少了css的体积
  4. 统一了样式和字体,使用专用的类名,就不存在每个地方的颜色和字体不一致的情况

缺点:

  1. 需要一点的记忆成本
  2. 和原本的css开发有点不同,需要适应
  3. 打包构建处理不当,会导致开发环境与生产环境样式不一致
  4. 构建不当,会导致包体积过大

工作实践

上面我们大概看了一下这个工具的用法以及优缺点,可能大家有许多的疑问,这里总结一下使用中的经验,希望能帮助各位对这个工具有一个客观的认知。

原子类最大的问题就是,体量非常的巨大,如何在生产环境中使用?

是的,原子类非常的巨大,在完全导入的情况下,tailwind css 2版本打包后的css文件大小达到了7M,这很显然是无法在网页中使用的,但是,tailwind 提供了生产环境剔除的功能,只需要指定需要匹配剔除的文件,生产环境打包会自动剔除没有使用的css.
但是实际上,2版本中,剔除问题有很大的问题,相信大家在使用vue或者react的过程中,都使用过,动态的类名如下:

<div :class=[class1,class2]></div>
<div :class="{{'active': isActive}}"></div>
<div :class="clase-{{claseename}}"></div>
复制代码

这样的操作,tailwindcss 就无法识别需要保留的css,导致生产环境与开发环境不一致。tailwind 官方文档也建议不要这样写,但是在3版本中,这个问题被解决了,因为3版本使用jit即时编译技术,这种动态类名也可以正常使用了。

以往都使用预处理,是否引入tailwind css后就抛弃预处理器?

在官方文档中,官方建议以postCss作为预处理器,其实就是说,直接使用css。但是我们要知道,这只是一个工具,他的优点是减少类名和css代码,所以我们可以以其为主导,在必要的时候,直接在类中加入css代码即可。 如下,我们可以大部分使用tailwind,在需要的时候,手动编写css

image.png 或者是两者联合使用

.bl-switch__main-core {
  height: 20px;
  width: 40px;
  transition: all 0.3s ease-in-out;
  @apply inline-block relative rounded-full cursor-pointer;
}
复制代码

因为tailwind 总有没有定制到的属性,所以有一些要求比较高的属性,我们可以直接手写css.在tailwind css 3中,因为启用jit技术,所以已经可以像这样使用了(3以下版本无法这样使用):

<div class="w-[12px]"></div>
复制代码

在tailwind不太擅长的领域,我们可以用预处理器补充,有需要循环,判断的操作,或者繁琐的选择器,我们都可以借助预处理器的功能,然后在编写样式的时候,使用tailwind.如下,就是一个input组件,在被激活焦点时候,改变父元素样式的功能

.bl-input__wrapper {
  box-shadow: 0 0 0 1px #ccc;
  @apply inline-flex rounded transition box-border flex-grow items-center justify-center px-2;
  &:not(.is-disabled):focus-within {
    box-shadow: 0 0 0 1px #43c6f5 !important;
  }
  &:not(.is-disabled):hover {
    box-shadow: 0 0 0 1px #a1a1a1;
  }
}
复制代码

当时编写的时候,发现tailwindcss 对阴影的操作不够灵活,就直接写css了,所以说,不要纠结工具与范式。

使用tailwind后,@apply 串联了一堆的样式,可读性差,给人看花眼了?

让我们先来看一下正常情况下的less代码,可能大神结构会好一点,这里只是一个简单的样式,使用的flex,当然flex是工具类,就没有写在这里了,因为内部的一些小差别,导致了一堆不同的类名,而一个类名下面仅仅也只写了一两个属性。

image.png 这个看起来头晕么,也晕,写的时候还好,写完一看,这TM啥啊。 所以我觉得,其实用tailwind或者不用,都逃避不开css看起来眼花的结局。而使用tailwind有一个好处就是,像那种只有一两个属性的类名,我们直接在html上面就写了,这样也不会增加复杂度,到时候后面再看,就在html上边看也是一目了然。
所以我觉得,最好的方式就是,样式多的,使用@apply抽下来,样式少的,直接写在html上边,而且一个类里面可以写多个@apply,我们可以把功能相似的属性写在一起,如果有空打个注释的话,以后再看就非常方便,不像以前写css那些属性都是想到一条写一条。

/*按钮基础属性*/
.bl-button {
    /*布局 高度*/
    @apply inline-flex justify-center items-center h-8;
    /*边距*/
    @apply px-3.5 py-2;
    /*边框*/
    @apply border rounded;
    /*字体*/
    @apply text-sm text-gray-700;
    @apply transition cursor-pointer whitespace-nowrap;
}
复制代码

像这样,是不是看起来就非常方便了呢?

维护性是否不好?

这个问题就和上面的可读性问题是一个道理,写css或者预处理器,也有写的一大堆的,这就好比,没有最好的框架只有最好的设计,一个再好的范式,也可能被人写成屎山。而且css写的好不好很多时候和html的结构也有直接的关系,html结构错乱,js逻辑混乱,css也好不到哪里去,总的来说,还是看人。
如果是新人来,看到一大堆嵌套的像递归调用一样的less/scss,那花几分钟看看tailwindcss的文档似乎也不是那么的困难。而且这个东西本来语义和css属性也差不多。

最后

无论如何,是否使用原子类,还是要结合自己的实际情况,毕竟这也是一种不一样的写css的思路,至于使用与否,如何使用,就仁者见仁智者见智了。技术无所谓好坏,存在即合理。

分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改