为什么选择 Tailwind CSS
我不是 Tailwind 的托儿。我是个“过来人” ——最近改变了看法,尽管承认它存在一些弊端,但我还是个乐于使用的人。“跨界人士”往往比天生的信徒更能说服怀疑论者。所以,如果您愿意,我希望为讨论贡献我的观点。
不久前,Adam Wathan问道:“在实际用 Tailwind 构建一些东西之前,您是否认为它是个糟糕的想法?”
我回答道:
我曾经向@samselikoff抱怨说,Tailwind 导致了丑陋且难以阅读的类名混乱,并表示零运行时 CSS-in-JS 可以以较低的学习曲线做更多的事情。
我错了两个方面:Tailwind 比我想象的更容易学习,而 CSSinJS 的灵活性可能是一个缺点。
在使用Tailwind完成了几个项目(包括我的个人网站和图书网站)之后,我觉得应该记录一下我喜欢它的地方。由于 Tailwind 是主流的实用 CSS 框架,也是我唯一尝试过的框架,所以我不会费力地将以下几点与实用 CSS 的普遍优势区分开来(但这里列出了其他一些优点)。
TL;DR
- “系统”值减少魔术数字:减少硬编码值,增加一致性。
- 浏览器中的响应式设计:在浏览器中创建原型,复制并粘贴到代码库,使用一致的系统值。
- 内联样式针对变化进行优化:通过消除对级联的所有依赖,使代码易于删除和移动。
- 内联样式减少了命名:通过解决计算机科学中已知的难题之一来更快地交付!
- 零 JS 和 CSS 的亚线性缩放:以 O(log N) 进行缩放,而不是 O(N)。
- 实用优先,而非仅实用:尊重最小功率原则,仅在必要时使用 CSS-in-JS。
最后我还介绍了对 Tailwind 的常见反对意见,并附上了他们的论点链接,以便您可以自己判断。
实用至上的佳能
在听我讲话之前,您可能希望先查看一下目前 CSS 中“实用程序类”革命中最具影响力的文章:
- Adam Wathan 的博客文章:CSS 实用类和“关注点分离”
- Simon Vrachilotis 的演讲《A Real Life Journey into the Opinionated World of Utility-First CSS》(另见:Sarah Dayan 的演讲)
我深受这些人和其他人的影响,所以如果我在下文中复述某些观点时表达得不好,那都是我的错。至少我先提供了正典资料。
“系统”值减少魔法数字
CSS 极其灵活,这让它变得强大,但也给你带来了许多陷阱。约束是必要的,否则我们就会把“魔法数字”撒满整个代码库。
Chris Coyier 认为CSS 中的魔法数字并非好事。他将其定义为“在某些情况下有效,但在情况发生变化时却脆弱且容易失效的值”。但坦白说,任何硬编码的数字,例如px
边距、媒体查询或颜色变量中的数字,在大规模情况下都难以妥善管理。人们很容易为了修复问题而违反规则,而且当设计需求发生变化时,重构的难度也非常高。如果你是独立工作,没有什么可以强制你坚持一套设计良好的统一数字尺度,这可能会导致设计看起来很糟糕。
当然,解决方案是只从预设的数值范围内进行绘制,我称之为“系统”(请注意,我不称之为“设计系统”,我对这方面的争论毫无兴趣)。Tailwind 默认自带一套优秀的字体和颜色系统。同样,你可以尝试使用 CSS 变量来构建自己的系统,但这样做的语法很繁琐,你仍然需要为类和变量命名,最终得到的是一个无法跨项目迁移的自定义系统,而且可能没有完善的文档。不妨在你的团队中引入Steve Schoger,采用一套优秀的默认系统。
存在替代方案:jxnblk -verse中的Styled-System和Theme UI是 CSS-in-JS 领域的基础。
浏览器中的响应式设计
2021 年编辑:Tailwind 的新 JIT 模式现在否定了这一点 :(我们的讨论在这里
这一点与从事设计的开发人员最为相关:最佳的开发工作流程是先在 上预览您的网站localhost
,然后在浏览器中进行调整,直到您满意为止,然后将更改直接复制粘贴到您的代码库中。我们称之为“浏览器中的设计”工作流程。
如果您想要这种工作流程,那么您就无法使用 React 的内联样式(这需要您使用对象语法)。但是,假设您确实使用了某种形式的“Write Real CSS”™ 解决方案,例如 Styled-Components、Vue 或 Svelte 作用域样式,这些方案可以实现浏览器内设计。Tailwind 还能为您提供什么呢?
- 您可以在浏览器中进行原型设计时直接从预设的“系统”值(上面详细说明)中提取
- 您也可以在浏览器中进行响应式和伪类设计 - 例如,在不同的断点、悬停或聚焦时应用样式,您只需在内联上添加前缀,例如对于链接,即可
text-green-400 hover:text-green-300 md:text-blue-400
。
我在最近的视频中对此进行了演示:
在浏览器中进行设计并不完全是 Bret-Victor 式的“原则发明”,但通过再次缩短认知距离,你至少更接近能够在情境中“玩耍”。使用 Tailwind,你甚至可以在玩游戏时内联添加过渡和动画。这一点被严重低估了——如果原型设计和添加动画更容易,我们开发者可能会在应用中提供更多动效。
内联样式针对变化进行优化
重要提示:实用程序类与内联样式不同- 我使用“内联样式”作为简写,但实际情况更加微妙,并且非常有利于实用程序类。
很多生产环境的 CSS都是追加式的。这是因为CSS 和它所影响的标记之间的认知距离通常很远——有时在不同的文件夹、不同的文件,或者同一个文件,但相隔几十行。除此之外,你还必须记住 CSS 的级联,并在脑子里把每个元素与所有匹配的规则进行匹配。
很快,你就会有一份连打开都不敢打开的代码库!开发人员的开发速度会变慢,最终你会陷入一个只有完全重写才能解决的困境。
CSS 的设计初衷是易于扩展。只需添加特定性即可!但删除却并非易事。这增加了复杂性,用Rich Hickey 的话说就是(因为 CSS 中位置很重要,所以你现在必须记住所有位置)。搭建纸牌屋很容易,但只要去掉一个元素,整栋建筑就可能轰然倒塌,而你只有在检查视觉回归或在脑海中模拟浏览器时才会意识到。
您可以使用工具(CSS 模块、JS 中的静态 CSS、Vue 或 Svelte作用域样式)或命名约定(BEM 等)来控制特异性,但这会缩短认知距离,而不是消除它。唯一能避免“远距离怪异行为”的选择是内联样式。内联样式可以针对变化进行优化。
“Galaxy Brain” 时间:Tailwind 为开发人员提供了内联样式的速度优势,而没有其缺点。
存在替代方案:其他解决方案,例如 Emotion 的
css
prop,styled-jsx
提供了内联样式的类似好处,但它们遇到了 JS 中标准 CSS 的缺点
内联样式减少命名
命名是一个众所周知的难题。我们浪费了大量时间在类的命名上。使用 Styled-Components 时,你经常需要编写一堆需要命名的中间样式组件。而使用BEM--
时,我们只需要解决三个半命名问题,而不是一个命名问题(我曾经因为是否应该使用 BEM或BEM而提交 PR 被搁置__
——真是浪费时间)。我们每年在命名上浪费了多少开发者的时间?
使用实用 CSS,我们可以显著减少代码库中的名称总数,或许更重要的是,减少我们需要独立创建和记忆的名称数量。这在你使用过没有这种问题的代码库之前,可能感觉并不重要。为了消除已知的难题,你愿意付出什么代价?我可没开玩笑——这是一个值得探讨的话题。名称对机器来说无关紧要,但对人类来说却很重要。
缺点是你必须从实用 CSS 框架中学习命名。如果没有文档-mb-5
,这些space-x-reverse
命名将无法解析。区别在于,传统的 CSS 命名是针对每个项目定制的,而 Tailwind 只需学习一次,就可以在每个项目中使用它们。没错,你可以尝试自己开发实用工具,但 Tailwind 的命名可能比你设想的命名更经过深思熟虑。
存在替代方案:情感的
css
道具,styled-jsx
也让你跳过命名。
零 JS 和 CSS 亚线性缩放
关于在 JS 中使用 CSS 的性能权衡及其缓解因素,已经有很多文章进行了探讨。您可以查看我的“React 新功能”演讲了解更多信息,但请放心,这个问题正受到双方充满热情和智慧的人士的热烈讨论。但我们都同意,JS 代码越少越好,并且我们也同意,按字节计算,传输 1kb 的 JS 代码对性能的影响远大于 1kb 的 CSS 代码。这些都很容易理解。
我热衷于探索的一点是,许多 CSS 和 CSS in JS 解决方案会随着应用中组件数量的增加而线性增长。由于 CSS 将每个声明的作用域限定在标识符内,因此您必须在想要应用它的所有地方重复它。这就是为什么我们font-weight: bold
之前工作的地方会有超过 50 个声明。单独来看,这些声明无关紧要,但批量处理起来,就麻烦了。
在我们的旧网站上,我们加载了超过 400 KB 的压缩 CSS(未压缩时为 2 MB)……我们一开始并没有这么多 CSS;它只是随着时间的推移而增长,很少减少。发生这种情况的部分原因是每个新功能都意味着添加新的 CSS。—— Facebook 工程部
您可以通过代码拆分来推迟这个问题,但最终人们会发布并实施一些黑客解决方法,以至于 CSS 再次失控(特别是如果它是仅附加的!)。
这里的解决方案是(可以说!)提供“原子”CSS,这样你的 CSS 就可以按O(log N)
(而不是O(N)
(N
你的组件数量))进行扩展。Facebook尚未发布的 stylex 库允许你用 JS 编写 CSS 并为你生成原子 CSS,但你也可以选择手写原子 CSS,就像 Tailwind 这样的实用框架指导你做的那样。
公平地说,我把这一点放在最后,是因为大多数应用不太可能发展到真正需要关注的规模,尤其是在考虑gzip 的情况下。然而,就像所有优化一样,这些事情在真正开始变得重要之前都是不成熟的。
效用优先,而非仅效用
除了上述所有优势之外,您仍然可以使用其他解决方案来获得所需的优势。例如,没有什么比 JS 中的 CSS 更强大了,您可以根据任意 JavaScript 动态更改媒体查询值和整个规则集。
然而,实际生活中真正需要这样做的场景有限,而且如果仅将其用于静态样式,成本(例如 JS 代码量)将占主导地位。追求实用性优先,此处遵循最小功耗原则。
非 CSS-in-JS 解决方案通常也更容易调试。这里的“容易”当然是主观的。但是,当 CSS-in-JS 解决方案出现问题时,我经常发现自己不得不查找文档,然后是 GitHub 问题,最后才开始深入研究node_modules
,这显然与我真正想要做的事情大相径庭。当 Tailwind 出现问题时,我知道我要么生成了预期的类名,要么没有生成。差异点要少得多。但理所当然的是,如果可以的话,大多数情况下你应该使用更容易调试的解决方案。
不好的部分
Tailwind 完美吗?当然不是。但它的优点大于缺点:
- 设置 Tailwind 意味着需要摆弄构建工具。这正在变得越来越容易,并且与其他解决方案实现类似性能所需的工具相当,但对某些人来说是不可接受的。
- Tailwind API 接口非常庞大,并且还在不断增长。这可以理解(因为它必须映射所有 CSS!),但学习和掌握起来也很累。最终的结果是,你需要支付一些前期学习成本,以期获得长期的生产力提升。这很好,但并非纯粹的胜利。
- 类名确实变得相当冗长。如果能有像 这样的简写就好了
md:hover:(text-green-300 underline border-5)
,但那样只会增加 API 的表面积。(未来编辑:现在已在twind
CSS in JS 中实现了 tailwind!)或许@apply
有必要使用 ,或者只是一些巧妙的排版。但总的来说,我发现内联编辑的便捷性压倒了表面的美观性。用户当然不在乎。 - CSS 抽象泄漏 - Tailwind 允许您像使用内联样式一样使用类,但它们在一个关键方面并非内联样式——当它们发生冲突时会发生什么。例如,对于“m-4 m-2”,“m-4”优先。Tailwind 生成的类的顺序很重要,即使它对您不可见。这是一个抽象泄漏。如果您正在尝试为内部设计系统构建可复用的组件,这与此背道而驰。您不妨探索一下 Chakra UI。
- 项目治理归 Tailwind Labs 所有——他们目前当然是项目发起人和重要管理者,但其流程并不像CSSWG那样,拥有既定的平等行为准则。与所有 BDFL 关系一样,除非你与他们意见相左,否则一切都会顺利。
- (小问题)VS Code 扩展仍然不够健壮——需要空格才能启动,而且经常无法正常工作。不过Brad Cornes正在努力解决这个问题!
结论:金发姑娘造型解决方案
最重要的是,我认为选择 Tailwind 取决于个人喜好,而非客观的正确答案。市面上有很多样式方案,有的自以为是,有的则不然。我最近是这样说的:
- 预制组件库限制太多,原生 CSS 太宽松
- JS 中的 CSS 太重,内联 CSS 功能不足
- 我们想要设计系统约束,但我们利用设计系统的方式非常严格(绑定到框架)
- Ben Holmes:“对于追求自由的设计师和追求结构的开发者来说,这是一个坚实的中间地带”。
Tailwind 适合那些想要“不太热,也不太冷”的造型解决方案的人。
金发姑娘解决方案。
附录:相反的观点
- Max Stoiber:为什么我用 JavaScript 编写 CSS
- Johan Ronsse:为什么你可能会后悔使用 Tailwind
- Aleksandr Hovhannisyan:为什么我不喜欢 Tailwind CSS -我的回复如下
- Jared White:为什么 Tailwind CSS 不适合我
- 如果你有值得关注的“为什么不使用 Tailwind”或“为什么我在了解 Tailwind 的情况下仍然使用其他工具”的帖子,请告诉我,我会将其包含在这里
- 在最近的 Svelte Radio 节目中(52 分钟),Kev提到大家在工具集成方面遇到了一些困难。随着时间的推移,这个问题将会得到更完善的记录。
处理异议(2021 年 1 月编辑)
- 但是 HTML 非常丑陋:代码美观度应该让位于开发速度和提供更好的用户体验的能力。
- 但是 CSS 变量存在!:是的,但是您无法获得内联样式的不间断工作流程。
margin-bottom: var(--spacing-8)
不等同于class="mb-8"
。 - 但是 Web 组件确实存在!:当然,你可以同时使用两者,但你的应用到底有多少是由 Web 组件组成的呢?对于那些开发非主流 Web 组件应用的人来说,Tailwind 是一个不错的选择。
- 我不喜欢
@apply
:好吧,反正你也不应该用太多。不过,降级@apply
到它的 CSS 组件其实很简单。