作者:张泽慧
目录
- 原子/功能类优先
- 从Semantic CSS说起
- 迎接Atomic/Utility-First CSS
- 丰富的功能类
- 生产优化
- 配置Purge
- 即时模式JIT
- 开发实践
- 使用Playground
- 在React中使用
- 在vs code中使用
原子/功能类优先
从Semantic CSS说起
要完成一个名片的样式开发,我们通常会这样写:在html或jsx结构中添加赋有语义化的class类名,随后在css中写入对应类的样式。
这是最常见、最常规的写法,被称作Semantic CSS(语义化CSS)规范。在这种规范下,我们追求关注点分离,让结构与样式各司其职,使结构更具语义化。但这样一来,在很多场景下也面临着许多问题,比如:
-
每一个标签都有一个专有的class,但就算是给标签添加一个小小的样式时,我们都得绞尽脑汁想出一个类名,既要求有语义化、符合代码规范,还要和与它作用相似的标签类名有区分;
-
每个类中往往会有很多个样式规则,在结构的语义、样式完全相同时才能做到真正的复用,若存在一点差异,就难以实现样式复用;
-
可以通过在html或jsx中删除类名的方式去掉相应的样式,但此时我们无法轻易的删除该类下的css样式,因为我们无法保证该类在别处也使用过,而在修改某个类中的样式时也存在同样的问题;
-
如果将html或jsx结构做迁移,我们还要将相应css同时迁移,即使这样,迁移后的样式也可能会根据上下文环境变得错乱;
-
......

Bootstrap可以视为Semantic CSS(语义化css)为基础的组件化框架。以按钮组件为例,它预定义了各种按钮样式,将不同样式封装成不同的类组件,并且每个样式都有自己的语义目的,比如success成功就为绿色,danger危险就代表红色......在使用时,我们不用再关注繁琐的css,引用相应的类名即可。但这样组件化的css,若想实现自定义的样式,可能需要更多的精力去做样式覆盖。

迎接Atomic/Utility-First CSS
Atomic/Utility-First CSS与 Semantic CSS 相对,Utility-First CSS(功能类优先css)不像Semantic CSS(语义化css)那样将组件样式放在一个类中,而是为我们提供一个由不同功能类组成的工具箱,我们可以将它们混合在一起应用在html元素上。对于Atomic CSS(原子css)来说,在物理世界中, 原子构成一般物质的最小单位,而在css世界中,则是一个类中只有唯一的css规则。
<style>
/* Atomic/Utility-First CSS */
.bg-black {
background-color: black;
}
.text-white {
color: white;
}
.rounded-8 {
border-radius: 8px;
}
/* Utility-First CSS,非Atomic CSS */
.py-8 {
padding-top: 8px;
padding-bottom: 8px;
}
.px-5 {
padding-left: 20px;
padding-right: 20px;
}
</style>
<button class="bg-black text-white rounded-8 py-2 px-5">button</button>
本文主人公 Tailwind CSS ,用法与Bootstrap类似,都是通过类名来引用样式。但最大的区别,也是Tailwind CSS的核心,即它是一套以Atomic/Utility-First CSS为基础CSS框架。同样是名片的样式开发,Tailwind CSS 这样做:

由此看来,基于Atomic/Utility-First CSS规范的Tailwind CSS框架的优点肉眼可见:
-
不用花时间想可恶的类名;
-
功能越简单的类,复用率越高;
-
结构与样式紧密耦合,不用来回切换html与css,更专注于html;
-
修改某个样式,不影响其他结构样式;
-
保证全局样式统一,不存在全局样式污染问题;
-
结构迁移时,对应的样式依然存在;
-
丰富的功能类,覆盖大多数的开发场景;
-
......
但也可肉眼可见的发现一些问题:
-
引用的类也太多了吧。但在Tailwind也中可以通过@apply提取组件样式;
-
类名堆砌,都不能通过语义化类名得知元素的作用。但在组件化开发的今天,相对于组件中某个标签元素的作用,更重要的是了解组件本身的作用;
-
Tailwind功能类超多,体积并不小喔。但可以通过配置purge,删除没用的东西;
-
......
丰富的功能类
-
尺寸:在Tailwind中,1对应0.25rem,
py-2则代表y轴上padding的大小,即padding-top及padding-bottom为0.5rem; -
字号:使用sm/base/lg等表标识字号及行高大小,如
text-sm表示字号为14px,行高为20px; -
颜色:通过50-900的数字来表示某种颜色的深浅程度,如
text-gray-500,与色卡中#6B7280对应;

- 响应式设计
Tailwind根据常见的屏幕分辨率,设置默认的5个断点,分别有:sm(640px)、md(768px)、lg(1024px)、xl(1280px)、2xl(1536px)。
<div class="h-12 border sm:w-1/2 md:w-48"></div>
sm:w-1/2、 md:w-48相当于:
@media (min-width: 640px) {
.sm\:w-1\/2 {
width: 50%;
}
}
@media (min-width: 768px) {
.md\:w-48 {
width: 12rem/* 192px */;
}
}
- 悬浮等状态
Tailwind使用功能类为各种状态的元素设置样式,包含Hover、Active、Focus、Disabled、 First-child、 Last-child等状态。
/* hover */
<button class="bg-red-500 hover:bg-red-700 ...">
Hover me
</button>
/* group-hover */
<div class="hover:bg-gray-800 group">
<h1 class="group-hover:text-white">title</h1>
<span class="text-gray-500 group-hover:text-white">i am content</span>
</div>
/* 与响应式前缀结合使用 */
<button class="... hover:bg-green-500 sm:hover:bg-blue-500">
Hover me
</button>
- 深色模式
Tailwind支持深色模式,仅需添加dark变体即可。使用该模式时需配置,默认不开启,以减小文件体积。
<div class="bg-white dark:bg-gray-800">
<p class="text-gray-900 dark:text-white">Dark mode is here!</p>
</div>
- 自定义配置
Tailwind可解决大部分的开发场景,此外,它还可以通过tailwind.config.js文件去覆盖原有配置,或增加自定义配置。
// tailwind.config.js
module.exports = {
purge: [],
darkMode: false, //默认不开启
theme: { // 主题
colors: {},
fontFamily: {},
extend: {},
},
variants: { //变体
extend: {},
},
plugins: [], // 插件
perset: [], // 预设
prefix: '', // 前缀
important: true, // 启用!important
...
}
- 更多Tailwind CSS精心设计的功能类,如弹性布局、背景、边框、过渡和动画等,在Tailwind CSS文档中拥有详尽的说明。
生产优化
相较于其他框架,Tailwind丰富的功能类让它有了一个不小的体积基数,但也正是因为丰富的功能类拥有单一的功能,促使它大大提高了复用率,新增的css就越少,并且页面越多,最终在打包体积上的收益就越大。此外,Tailwind还对生产作出优化,如配置purge以及其2.1版本上线的Just In Time即时模式。

配置Purge
Tailwind拥有成千上万的功能类,但不是所有都是必须的,我们可以通过在配置文件中设定purge选项,tree-shake 删除未使用的样式,优化最终构建大小。
-
enabled:是否启用,开发时可设为false;
-
content:检查的文件路径;
-
layers:清理特定层中的所有样式
module.exports = {
purge: {
enabled: true,
content:['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'],
layers: ['components', 'utilities'],
...
},
...
}
该功能的底层使用的是 PurgeCSS 库,它的清扫思路是:
- 扫描我们提供内容文件,针对特定文件类型,根据提取器提取出使用到的各种属性;
- 解析我们依赖的css文件,识别所有存在的选择器,生成AST抽象语法树;
- 提取到的信息与AST抽象语法树进行查找匹配,将未使用到的CSS规则从语法树中删掉;
- 最后在NODE_ENV为
production的情况下,构建生成的样式表只会留下用到的样式。
在Tailwind中,我们可以不用去设置提取器,Tailwind已经帮我们做好了一切,我们只需要配置需要清扫的文件路径即可。此外,PurgeCSS在扫描文件时,查找与正则表达式相匹配的任何字符串,所以我们在动态判断类名时,需写完整的类名,而不是让类名做动态的拼接,这样保证PurgeCSS可以找到使用过的类,避免意外删除掉重要的样式。
`/[^<>"'`\s]*[^<>"'`\s:]/g`
即时模式JIT
虽然PurgeCSS帮助我们在生产环境中清除掉了未使用到的类,但是在开发环境中,构建工具仍然生成所有的类。这样一来,Tailwind 生成的 CSS 包变得越来越大,应用程序的构建变得越来越慢,并且 Dev Tools由于要摄取的 CSS 量而变得缓慢,造成极高时间与极差的开发体验,而tailwind2.1版本中JIT模式为我们很好的解决了这个问题。
JIT与AOT的区别:
- JIT(Just In Time):运行时,在浏览器中编译应用程序;
- AOT(Ahead of Time):构建时,在服务器上编译应用程序。
JIT编译器不是在初始构建期间去提前创建所有内容,它会按需生成模版,监视我们的html文件,只会获得我们真正需要的css类。css体积很小,构建时间更短,Dev Tools响应更快。并且在以前,默认情况下禁用了许多Tailwind变体,因为它们会导致生成数兆字节的 css,但因为 JIT 会“按需”生成样式,默认情况下这些变体都是启用的,这意味着所有这些变体都可以零配置直接使用。
// tailwind.config.js
module.exports = {
mode: 'jit',
purge: [
// ...
],
theme: {
// ...
}
// ...
}
同时,JIT“捆绑”了一些更灵活的写法:
<!-- 支持任意值 -->
<button class="bg-[#1da1f1]">button</button>
<!-- 内置重要修饰符 -->
<p class="font-bold !font-medium">
This will be medium even though bold comes later in the CSS.
</p>
<!-- 可堆叠变体 -->
<button class="md:dark:disabled:focus:hover:bg-gray-400">
<!-- 颜色/不透明度简写 -->
<div class="bg-red-500/25">
and so on...
开发实践
使用Playground
在使用开发之前,可以先使用Tailwind官网提供的在线运行环境 Tailwind Play 尝尝鲜。在这里,可以即时编译你编写的代码、配置CSS和Config文件、给出Tailwind语法提示,鼠标悬浮在类名上会显示css源代码,完成后还能向别人分享你的作品链接。

在React中使用
在react项目中,安装tailwind、postcss以及autoprefixer,
- Tailwimd CSS 本质是 postcss 的一个插件(react自身安装了旧版本的postcss,故在此需指定安装对应的旧版本);
- 通过autoprefixer自动添加浏览器引擎前缀。
npm install tailwindcss@npm:@tailwindcss/postcss7-compat @tailwindcss/postcss7-compat postcss@^7 autoprefixer@^9
配置Craco
CRA脚手架将webpack配置封装到react-scripts的package中,默认不可见。当脚手架配置不满足需求时,可通过npm run eject命令暴露配置,交由我们自己管理。但eject操作一旦完成,就无法还原、无法升级react-scripts。而Craco工具可帮助我们不执行eject,即可进行webpack自定义配置、统一集中化管理所有的配置。
- 在react项目中安装Craco工具
npm install @craco/craco
- 在package.json文件中改写scripts部分
"scripts": {
"start": "craco start",
"build": "craco build",
"test": "craco test",
"eject": "react-scripts eject"
},
- 在根目录手动创建一个craco.config.js文件,并且添加tailwind和autoprefix作为PostCSS插件
module.exports = {
style: {
postcss: {
plugins: [
require('tailwindcss'),
require('autoprefixer'),
],
},
},
}
引入Tailwind CSS
执行命令初始化tailwind,初始化后在根目录下生成tailwind.config.js文件,可进行purge等自定义配置。
npx tailwind init
在src/index.css文件中,清空原有样式,使用@tailwind命令引入tailwind三大件
- **base(基础):**tailwind的基本样式,即重置浏览器样式;
- **components(组件):**tailwind定义的组件类;
- **utilities(功能):**tailwind的实用程序类,是tailwind核心。
@tailwind base;
@tailwind components;
@tailwind utilities;
或者,直接在js文件中导入 tailwindcss / tailwind.css
import "tailwindcss/tailwind.css"
在vs code中使用
在vs code中,安装 Tailwind CSS IntelliSense 插件,即可拥有智能代码提示:自动补全、语法检查、悬停预览、语法高亮显示;
此外,我们还可以根据 速查表 帮助我们减少记忆负担。
