Tailwind 入门
Tailwind介绍
前言
众所周知,CSS是一个相对简单且语法宽松的语言,虽然对开发者友好,但因为它过于“随意”且耦合性强(如height和line-height都会对元素的高度造成影响),如果开发时不做限制,对后续维护的人员来说将会造成“毁灭性打击”。
故为了样式代码的可读性,也为了减少维护人员受到的心理创伤,我们需要对CSS的编写做出限制
BEM命名规范 😀
一般来说,我们编写CSS时类名会遵循BEM命名规范,这种规范的特点是语义化、结构化、遵循开闭原则。参考知乎——CSS之BEM命名规范
“开闭原则”指对修改封闭、对扩展开放,是面向对象思想的重要基本原则
BEM规范存在的问题 😐
-
语义化的CSS类看似与HTML无关,但在嵌套的CSS选择器(如使用less时)中却反映出了具体的HTML结构。除此以外,CSS里还有很多会被父元素或子元素影响的属性,故将CSS与HTML解耦实际上是很困难甚至无法达成的
-
在处理两个外表相似的内容时(如下图的作者简介和文章预览),即使样式决策有99%是一致的,我们也很可能需要编写两个语义不同的类来赋予它们样式(如.author-bio和.article-preview),并且这两个类是几乎不可复用的
实际上,可以通过一些CSS预处理器的如 @extend 等功能实现复用,但会对后续维护造成很大的困扰
-
即便将类拆分到每个组件,使其不基于内容(如.card .button)虽然可以在一定程度上复用这些类,但当组件功能越具体,复用就会越困难(如.dialog-header__button)
-
给CSS类命名是令人费神的
原子类
原子类,指具有“原子性”的CSS类
在化学反应中“原子”是最小的单位,故“原子性”指具有不可拆解、不可更改的性质
示例:
.flex {
display: flex;
}
上面的.flex就是一个原子类,它的特点就是一个类名对应一个CSS规则,并且类名应该是和规则有关系的
原子类的思想可以说与我们常用的BEM规范“背道而驰”,BEM强调的是语义化与将CSS从HTML的关注点分离(实际上很可能没有分离),而原子类几乎摒弃了语义化,通过使用提供的大量工具类使页面符合最终的设计决策
Tailwind基本概念
Tailwind就是一个采用了CSS原子类和“All in JS”思想的框架
“All in JS”指“HTML in JS”和“CSS in JS”,其中“HTML in JS”就是Vue、React等框架在做的事
框架特点
-
Tailwind 提供了一组可重用的 CSS 类,可用于几乎任何类型的 UI 组件和布局。
-
Tailwind 不会预定义任何样式。相反,它将样式定义为独立的类,使开发人员可以按需使用它们。
-
Tailwind 还提供了广泛的定制选项,以便根据需要调整样式。这包括设置颜色,间距和字体等属性。通过修改配置文件,开发人员可以轻松地自定义 Tailwind 的外观和功能。
-
Tailwind 支持响应式设计。在 CSS 中处理一大堆复杂的媒体查询(media queries)是很糟糕的,而 Tailwind 能够让你 在 HTML 中直接支持响应式设计。
效果参考2023 GitHub最热门的前端UI项目——shadcn
基本原理
-
Tailwind 本质上是一个PostCSS的插件,PostCSS将CSS解析为JS可操作的AST语法树,再由Tailwind对解析出的语法树进行操作,生成最终的CSS文件
PostCSS 本身是一个功能比较单一的工具。它提供了一种方式用 JavaScript 代码来处理 CSS。它负责把 CSS 代码解析成抽象语法树结构(Abstract Syntax Tree,AST),再交由插件来进行处理。插件基于 CSS 代码的 AST 所能进行的操作是多种多样的,比如可以支持变量和混入(mixin),增加浏览器相关的声明前缀,或是把使用将来的 CSS 规范的样式规则转译(transpile)成当前的 CSS 规范支持的格式。
-
Tailwind 还依赖Autoprefixer,它也是一个PostCSS插件,用于为 CSS 中的属性添加浏览器特定的前缀(使用 Can I Use 网站提供的数据来确定所要添加的不同浏览器的前缀)
浏览器厂商在实现某个 CSS 新功能时,会使用特定的浏览器前缀来作为正式规范版本之前的实验性实现。比如 Webkit 核心的浏览器使用-webkit-,微软的 IE 使用-ms-。为了兼容不同浏览器的不同版本,在编写 CSS 样式规则声明时通常需要添加额外的带前缀的属性。这是一项繁琐而无趣的工作。
Autoprefixer 可以自动的完成这项工作。Autoprefixer 可以根据需要指定支持的浏览器类型和版本,自动添加所需的带前缀的属性声明。开发人员在编写 CSS 时只需要使用 CSS 规范中的标准属性名即可。
其他
-
与内联样式有什么区别?
- 有约束的设计 使用内联样式,每个值都是一个魔数。使用Tailwind,你可以从预定义的 设计系统 中选择样式,这使得构建视觉上一致的 UI 变得更加容易。
- 响应式设计 在内联样式中无法使用媒体查询,但可以使用 Tailwind 的 响应式工具轻松构建完全响应式界面。
- 悬停、焦点等状态 内联样式无法针对悬停或焦点等状态,但 Tailwind 的 状态变量可以轻松地使用工具类对这些状态进行样式设置。
基本使用
官方文档中对Tailwind框架的安装、配置与使用说明已经非常详细了,故不多做赘述。Tailwind中文官网
个人体验
优点:
- 在组件库有良好UI规范的情况下,配置好之后可以很方便的使组件开发符合UI规范,大幅减少写css的时间
- 免去给不同的容器起类名的时间(BEM规则可能导致类名很长且冗余),添加一个常用的规则时,直接加原子类,而不是定义一个类名,再去style里面写样式,熟悉之后可以大幅增加开发效率
- 不需要关注CSS的优先级。当DOM结构非常复杂时,难免写出很多复杂的选择器,进而带来意料之外的样式(这时大概率会用!important暴力解决)
- 实现响应式效果只需要简单的配置
- 完全解耦CSS,不用担心同名类样式冲突的问题(如在父组件中修改类样式时影响了子组件中的同名类)
- 天然的CSS体积优化,尽最大可能复用样式可以使CSS代码不会一直膨胀,也可以通过额外配置继续大幅减少打包后的css文件体积
- 避免编写一些无法确定是否能丢弃的样式,降低样式约定的成本
缺点:
-
使用初期有一定的记忆成本
-
相同属性覆盖关系默认不可控,需要手动处理或避免添加两个相同属性的原子类
如:若
class="... text-red text-blue"
,此时无法从代码判断文本最终会是红色还是蓝色,因为这由生成的css代码顺序决定,而非由编写类名的顺序决定,需要在@layer中指定类的生成顺序才能确定 -
原子类很难覆盖样式,需要确定组件在实际使用中是否有频繁的样式改动需求
-
对前端新人来说直接用tailwind可能会降低原生css代码能力
-
因为是全局样式,可能与已依赖的UI库(如element ui)存在个别样式冲突问题
-
只能匹配静态的类字符串,不能匹配动态生成的类,需要想方法解决状态变更问题(如切换type、size等)
总结
- Tailwind是一个优缺点都很明显的框架
- 适用于统一UI风格和快速开发业务组件
- 用于组件库时,需要注意组件库是否有明确的UI规范或高度的模块化。组件的功能越复杂,耦合性就越强,后续需要调整的可能性也会增大。这时应该考虑用原来的BEM规范编写CSS
当然,混合使用BEM规范与原子类框架也不会有什么冲突,如果既想简化开发,又想给样式覆盖加上兜底方案的话(但这样我们又得给类起名了☹️)