windicss使用记录

在去年年初写了一篇《关于TailwindCSS的一些思考》,记录了一下我对于原子类框架的看法,却一直没有机会在项目中使用,最近终于有时间来尝试一下原子类了。经过一段时间深度使用windicss,回头记录一下关于我对原子类框架新的看法(是如何从跃跃欲试到爱不释手的

本文也可以用于windicss入门使用的帮助,记录了大部分刚开始使用原子类框架的前端开发者可能会遇见的问题。

<!--more-->

参考

1. 基础使用

目前主流的原子类框架TailWindcss、windicss、unocss等,大部分类名规则是一致的,只是细节上有些差别。

因此,学习windicss的时候,可以通读一遍示例文档,有个初步的印象。由于windicss 文档不太全,有大致印象后,可以再去看看tailwindcss的文档上面看

下面整理一些比较常用的类

1.1. 布局

  • flex布局flexflex-colitems-centerjustify-center
  • 定位布局relativeabsolutefixedleft-10pxtop-20pxright-30pxbottom-40px
  • 网格布局gridgrid-cols-[100px,100px,100px]gap-30px
  • 元素类型 blockinline-blockinline

1.2. 尺寸

  • 宽度w-100pxw-[calc(100vw_-_20px)]
  • 高度h-100pxh-[calc(100vw_-_20px)]
  • 内边距
    • p-10px上下左右
    • top pt-10px、bottom pb-10px、left pl-10px、right pr-10px,各个方位
    • 水平 px-10px、竖直 py-10px
  • 外边距,命名与内边距基本一致,把p padding换成 m margin即可,如mt-10px
  • 边框 ,命名与内边距基本一致,换成border即可,如border-t-1pxborder-t-[#ff0000]
  • 圆角,rounded-10px,还有更简单的指定某个方位的圆角,命名与内边距基本一致,如rounded-t-10px

1.3. 颜色

  • 使用语义化颜色,如xxx-red-100xxx-dark-900,其中xxx可以是字体颜色text、背景颜色bg、边框颜色border

  • windicss内置了一套语义化颜色名称,可以在主题配置中自定义颜色

    export default defineConfig({
     theme: {
        extend: {
          colors: {
                  primary: '#00fff00',
              red: {
                100: '#123123',
                200: '#123123',
                600: '#123123',
              },
          }
        }
      }
    })
  • 同时也支持颜色字面量,如text-[#ff0000]

1.4. 字体

  • 字号text-16px
  • 行号leading-32px
  • 颜色text-red-100text-[#ff0000],参考上面的颜色章节
  • 换行whitespace-nowrap
  • 溢出line-clamp-2,需要配置插件line-clamp
// windicss.config.ts
import LineClamp from 'windicss/plugin/line-clamp'

export default defineConfig({
    plugins: [
    LineClamp,
  ]
})

1.5. 背景

  • 背景颜色bg-red-100bg-[#ff0000],参考上面的颜色章节

  • 透明度bg-opacity-50

  • 自定义渐变使用起来还是有点麻烦,我感觉渐变用行内样式更合适一点

  • 阴影同上,感觉也更适合用行内样式来实现

1.6. 伪类

想实现诸如not:(last-child)之类的选择器,就可以用文档 里面提到的

  • firstlastnot-firstnot-last常用于定制列表元素首个或末尾元素的样式,比如边距last:mb-0px
  • beforeafter伪元素,before:(w-100px h-100px bg-red-100)
  • smmdlgxl、媒介查询,可以方便实现断点sm:(w-100px) md:(w-200px)
  • darklight,实现暗夜模式的时候非常有用

1.7. important

所有样式前面加感叹号就可以加上!important,如!mt-10px

1.8. 暗黑模式

使用windicss实现暗黑模式非常简单,只需要在配置文件中配置darkModeclass,然后在根节点添加对应的类名dark即可。

对于原子类而言,添加dark:前缀即可,比如dark:text-white-300表示在黑夜模式下字体颜色为text-white-300

1.9. 嵌套选择器

在以类为主的选择器中,有一种比较常见的实用场景,那就是通过父类来控制子类的样式和行为,一个比较经典的场景是:比如鼠标移动到父元素,子元素执行transform等动画

或者是父类设置了disable,后代子节点的样式会发生变化,类似于dark的功能。

查看了文档,貌似并没有类似父元素控制子元素样式的功能disable:(text-grey-500 bg-grey-100)

最后,在这个issue,看到了自定义伪类的大致实现

import plugin from 'windicss/plugin'

export const ChildCombinator = plugin(({ addVariant }) => {
  addVariant('child', ({ modifySelectors }) => {
    return modifySelectors(({ className }) => {
      console.log(className)
      return `.parent .${className}`
    })
  })
})

然后在defineConfig的plugin中引入这个插件,就可以使用嵌套选择器了

  <div class="w-500px">
    <div class="parent">
      <div>
        <div class="child:(w-100px h-100px bg-red-100)"></div>
      </div>
    </div>
    <div>
      <div>
        <div class="child:(w-100px h-100px bg-red-100)"></div>
      </div>
    </div>
  </div>

比如上面的代码,就只有第一个child:(w-100px h-100px bg-red-100)的样式会生效

2. 进阶用法

2.1. 字面量Or语义化

TailwindCSS认为,所有不使用其变量的值都为魔法值,因此大部分值最好都在配置文件中先定义好。

但实际场景是,设计师如果没有提前定义好各种值,而是随心所欲,这个配置文件就会越来越长,越来越难维护

如果想要使用配置定义之外的值,比如某个宽度宽度为132px的元素,可以写成w-132px,不过这种写法会导致后续维护性变差,需要权衡一下

参考文档,可以通过windicss-analysis这个工具来分析整个项目中的原子类,检查类名是否符合设计系统。

编写原子类很简单,但要编写容易维护的原子类并不是一件很容易的事情,这需要前端和设计在预设的设计系统中共同合作才行。

2.2. 自定义计算规则

比如p-{float},默认计算为

p-{float} -> padding: {float/4}rem;

期望有自己的计算规则,比如在pc端直接解析为px

p-{float} -> padding: {float}px;

在移动端解析为更特殊的rem

p-{float} -> padding: {calc(float)}rem;

windicss本身并没有提供这个功能,但post插件貌似可以通过一些途径解决这个问题

比如px2rem等插件,可以将text-16px自动转换成

.text-16px {
    font-size: 0.16rem;
}

因此,同样的text-16px的代码,在移动就可以按照rem来计算最终的数值,在PC端还是按照原样px计算。

按照原则,使用原子类就应该尽量避免再手写css代码,既然没有css代码,就完全不需要预处理器了。

但实际业务中,还是存在原子类无法覆盖的情况,如何兼容两个工具,也需要斟酌一下。

2.3. 减少js中的css字符串

原子类移除了源代码中的css文件,但会增加SPA项目中js文件的大小,因为所有的class都按照字符串的形式打包进了js文件。

也许这并不是一个很重要的问题,但如果愿意优化,下面是一些思路。

第一个优化点是减少class的字符是:class太长了,一堆一堆的,可以考虑属性化 attibute,参考 https://windicss.org/posts/attributify.html

<div font="italic leading-3" text="20px"></div>

使用attibutify需要对原子类有进一步的理解和归类,以及一些学习成本,如果是多人项目,需要考虑团队协作。

第二是优化点事减少class的数量:比如利用css的继承属性,将一些可继承的样式对应的原子类放在公共的父节点上面

此外,还可以通过@apply等指令创建公共的类名,合并大段可复用的样式。

3. 小结

使用原子类最大的优点是不用在html和css代码段之间来回切换,看见html和行类的原子类名,就能大概知道这个元素对应的样式。html删除了,样式就删除了,也不会存在css冗余的情况。

原子类不存在样式重复和覆盖的情况,也就不再需要scoped css 或者css module等技术。这在编写jsx文件的时候非常有用,这才是真正的HTML、CSS、JS三合一!

此外,也不用再为了复用UI来强行封装逻辑差异比较大的组件,组件的复用,复制原子类和html代码是更合理的做法。

当然,原子类最大的问题也很明显,就是上面提到的字面量可维护性很差的问题,试想一下,如果项目中充斥了一个基础主题色text-[#ff0000],如果设计师想把这个色值调整成text-[#00ff00],在没有预先设定语义名字的情况下,这种更改就要修改很多个文件。换言之,使用原子类需要对设计团队和开发团队需要提前规划和沟通。

接下来,我会在项目中进一步推进和深度使用windicss,有遇见其他的问题会继续补充。