[{"data":1,"prerenderedAt":965},["ShallowReactive",2],{"\u002Fwriting\u002Fcss-bem-or-atom":3},{"id":4,"title":5,"body":6,"date":953,"description":954,"extension":955,"intro":956,"meta":957,"navigation":326,"path":958,"seo":959,"stem":960,"tags":961,"__hash__":964},"writing\u002Fwriting\u002Fcss-bem-or-atom.md","当谈论 CSS 架构时，我们在谈论什么",{"type":7,"value":8,"toc":942},"minimark",[9,21,33,38,41,49,95,98,102,110,115,135,142,145,506,509,535,578,587,591,594,627,630,642,645,648,680,683,686,693,707,714,717,720,723,732,735,739,742,880,883,886,903,906,909,916,932,935,938],[10,11,12,13,20],"p",{},"无论 web 开发框架如何层出不穷、日新月异，浏览器始终只能识别 HTML、CSS 和 JS。前端工程师们对于 CSS 的讨论逐渐从「选择器优雅」「语义化类名」迁移到了「可组合性」「构建时优化」和「工程效率」。在过去 ",[14,15,19],"a",{"href":16,"rel":17},"https:\u002F\u002Fgetbem.com\u002F",[18],"nofollow","BEM","（Block-Element-Modifier）代表了对中大型协作项目中可维护性的工程化回答；但在近年来，以 Tailwind 为代表的 utility-first 工具异军突起，Tailwind 也成为了社区中最受欢迎的 CSS 技术之一。两者的策略和风格截然不同，但随着越来越多的项目使用了 Tailwind，我们不得不认同它正在重塑 CSS 的架构。",[22,23,25],"callout",{"title":24},"强烈推荐！",[10,26,27,32],{},[14,28,31],{"href":29,"rel":30},"http:\u002F\u002Fwww.csszengarden.com\u002F",[18],"CSS Zen Garden"," 是关注点分离 SoC 的绝妙展现。这项工程的所有示例页面均使用了完全相同的 HTML 结构，仅靠替换 CSS 文件就能实现完全不同的外观和视觉风格。",[34,35,37],"h2",{"id":36},"回顾bem-的工程承诺","回顾：BEM 的工程承诺",[10,39,40],{},"在 SPA 尚未普及的时代，全局 CSS 在提供了共享样式的便利的同时，也使样式冲突、层叠和可预测性差等问题变得更加显著。BEM 的核心理念十分明确：使用更长但容易预料、遵循模式的类名来组织 CSS。",[10,42,43,44,48],{},"在 BEM 的观点中，所有的类名应当是",[45,46,47],"code",{},"block__element--modifier","的格式。其中，B（Block）指可以独立存在的实体，例如菜单 menu、列表 list、复选框 checkbox 等；E（Element）是指 block 的一部分，依赖于 block 存在，例如 menu item，list entry，checkbox caption 等；M（Modifier）则是表示元素状态的修饰，例如 disabled、active、hover 等。",[50,51,56],"pre",{"className":52,"code":53,"language":54,"meta":55,"style":55},"language-css shiki shiki-themes houston",".container {}\n.container__header {}\n.container__action {}\n.container__action--disabled {}\n","css","",[45,57,58,71,79,87],{"__ignoreMap":55},[59,60,63,67],"span",{"class":61,"line":62},"line",1,[59,64,66],{"class":65},"sb16X",".container",[59,68,70],{"class":69},"sLo_3"," {}\n",[59,72,74,77],{"class":61,"line":73},2,[59,75,76],{"class":65},".container__header",[59,78,70],{"class":69},[59,80,82,85],{"class":61,"line":81},3,[59,83,84],{"class":65},".container__action",[59,86,70],{"class":69},[59,88,90,93],{"class":61,"line":89},4,[59,91,92],{"class":65},".container__action--disabled",[59,94,70],{"class":69},[10,96,97],{},"BEM 的贡献在于，它提供了一套足够简单易懂的规范，让团队可以以统一的模式进行样式层面的管理。增加了长度和复杂性的类名很大程度上避免了命名冲突，进一步化解了层叠性和优先级的问题。在团队全部遵循严格的命名规范后，样式“看起来”像是局部的，使所有成员可以在不同模块间独立工作。从结果上讲，这些优点在大型团队中十分实用。很长一段时间里，甚至直到组件化框架已经成型后，BEM 方法论依然被无数团队采纳。",[34,99,101],{"id":100},"bem-的缺陷","BEM 的缺陷",[10,103,104,105,109],{},"BEM 本质上是一种",[106,107,108],"strong",{},"约定式架构","，把复杂性转移到了约定与命名成本上。随着项目规模的扩大和组件化框架的诞生，这种成本催生了其他的问题。",[111,112,114],"h3",{"id":113},"越具体越难复用","越具体，越难复用",[10,116,117,118,122,123,126,127,122,129,131,132,134],{},"BEM 在使用一种类似面向对象的概念编写样式。假设需要实现的两个组件：",[119,120,121],"em",{},"Author Card"," 和 ",[119,124,125],{},"Book Card","，如下图所示。其中，",[119,128,121],{},[119,130,125],{}," 均有横排和竖排两种布局形式，且 ",[119,133,125],{}," 拥有一种促销状态。",[10,136,137],{},[138,139],"img",{"alt":140,"src":141},"cards","\u002Fimg\u002Fatomic-css\u002Fcards.png",[10,143,144],{},"采用 BEM 规范时，两个组件的 HTML 结构也许是下面这样的：",[50,146,150],{"className":147,"code":148,"language":149,"meta":55,"style":55},"language-html shiki shiki-themes houston","\u003Cdiv class=\"author-card\">\n  \u003Cdiv class=\"author-card__image\">\u003C\u002Fdiv>\n  \u003Cdiv class=\"author-card__content\">\n    \u003Cdiv class=\"author-card__title\">Agatha Christie\u003C\u002Fdiv>\n    \u003Cdiv class=\"author-card__intro\">...\u003C\u002Fdiv>\n    \u003Cdiv class=\"author-card__actions\">\n      \u003Cbutton class=\"author-card__action--primary\">MORE\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\n\n\u003Cdiv class=\"book-card\">\n  \u003Cdiv class=\"book-card__image\">\u003C\u002Fdiv>\n  \u003Cdiv class=\"book-card__content\">\n    \u003Cdiv class=\"book-card__title\">And Then There Were None\u003C\u002Fdiv>\n    \u003Cdiv class=\"book-card__intro\">...\u003C\u002Fdiv>\n    \u003Cdiv class=\"book-card__actions\">\n      \u003Cbutton class=\"book-card__action--primary\">Add to Cart\u003C\u002Fbutton>\n      \u003Cbutton class=\"book-card__action--secondary\">MORe\u003C\u002Fbutton>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\n","html",[45,151,152,174,195,210,231,252,268,291,301,311,321,328,344,364,380,401,421,437,458,479,488,497],{"__ignoreMap":55},[59,153,154,157,161,165,168,171],{"class":61,"line":62},[59,155,156],{"class":69},"\u003C",[59,158,160],{"class":159},"sqF6k","div",[59,162,164],{"class":163},"s8HWQ"," class",[59,166,167],{"class":69},"=",[59,169,170],{"class":65},"\"author-card\"",[59,172,173],{"class":69},">\n",[59,175,176,179,181,183,185,188,191,193],{"class":61,"line":73},[59,177,178],{"class":69},"  \u003C",[59,180,160],{"class":159},[59,182,164],{"class":163},[59,184,167],{"class":69},[59,186,187],{"class":65},"\"author-card__image\"",[59,189,190],{"class":69},">\u003C\u002F",[59,192,160],{"class":159},[59,194,173],{"class":69},[59,196,197,199,201,203,205,208],{"class":61,"line":81},[59,198,178],{"class":69},[59,200,160],{"class":159},[59,202,164],{"class":163},[59,204,167],{"class":69},[59,206,207],{"class":65},"\"author-card__content\"",[59,209,173],{"class":69},[59,211,212,215,217,219,221,224,227,229],{"class":61,"line":89},[59,213,214],{"class":69},"    \u003C",[59,216,160],{"class":159},[59,218,164],{"class":163},[59,220,167],{"class":69},[59,222,223],{"class":65},"\"author-card__title\"",[59,225,226],{"class":69},">Agatha Christie\u003C\u002F",[59,228,160],{"class":159},[59,230,173],{"class":69},[59,232,234,236,238,240,242,245,248,250],{"class":61,"line":233},5,[59,235,214],{"class":69},[59,237,160],{"class":159},[59,239,164],{"class":163},[59,241,167],{"class":69},[59,243,244],{"class":65},"\"author-card__intro\"",[59,246,247],{"class":69},">...\u003C\u002F",[59,249,160],{"class":159},[59,251,173],{"class":69},[59,253,255,257,259,261,263,266],{"class":61,"line":254},6,[59,256,214],{"class":69},[59,258,160],{"class":159},[59,260,164],{"class":163},[59,262,167],{"class":69},[59,264,265],{"class":65},"\"author-card__actions\"",[59,267,173],{"class":69},[59,269,271,274,277,279,281,284,287,289],{"class":61,"line":270},7,[59,272,273],{"class":69},"      \u003C",[59,275,276],{"class":159},"button",[59,278,164],{"class":163},[59,280,167],{"class":69},[59,282,283],{"class":65},"\"author-card__action--primary\"",[59,285,286],{"class":69},">MORE\u003C\u002F",[59,288,276],{"class":159},[59,290,173],{"class":69},[59,292,294,297,299],{"class":61,"line":293},8,[59,295,296],{"class":69},"    \u003C\u002F",[59,298,160],{"class":159},[59,300,173],{"class":69},[59,302,304,307,309],{"class":61,"line":303},9,[59,305,306],{"class":69},"  \u003C\u002F",[59,308,160],{"class":159},[59,310,173],{"class":69},[59,312,314,317,319],{"class":61,"line":313},10,[59,315,316],{"class":69},"\u003C\u002F",[59,318,160],{"class":159},[59,320,173],{"class":69},[59,322,324],{"class":61,"line":323},11,[59,325,327],{"emptyLinePlaceholder":326},true,"\n",[59,329,331,333,335,337,339,342],{"class":61,"line":330},12,[59,332,156],{"class":69},[59,334,160],{"class":159},[59,336,164],{"class":163},[59,338,167],{"class":69},[59,340,341],{"class":65},"\"book-card\"",[59,343,173],{"class":69},[59,345,347,349,351,353,355,358,360,362],{"class":61,"line":346},13,[59,348,178],{"class":69},[59,350,160],{"class":159},[59,352,164],{"class":163},[59,354,167],{"class":69},[59,356,357],{"class":65},"\"book-card__image\"",[59,359,190],{"class":69},[59,361,160],{"class":159},[59,363,173],{"class":69},[59,365,367,369,371,373,375,378],{"class":61,"line":366},14,[59,368,178],{"class":69},[59,370,160],{"class":159},[59,372,164],{"class":163},[59,374,167],{"class":69},[59,376,377],{"class":65},"\"book-card__content\"",[59,379,173],{"class":69},[59,381,383,385,387,389,391,394,397,399],{"class":61,"line":382},15,[59,384,214],{"class":69},[59,386,160],{"class":159},[59,388,164],{"class":163},[59,390,167],{"class":69},[59,392,393],{"class":65},"\"book-card__title\"",[59,395,396],{"class":69},">And Then There Were None\u003C\u002F",[59,398,160],{"class":159},[59,400,173],{"class":69},[59,402,404,406,408,410,412,415,417,419],{"class":61,"line":403},16,[59,405,214],{"class":69},[59,407,160],{"class":159},[59,409,164],{"class":163},[59,411,167],{"class":69},[59,413,414],{"class":65},"\"book-card__intro\"",[59,416,247],{"class":69},[59,418,160],{"class":159},[59,420,173],{"class":69},[59,422,424,426,428,430,432,435],{"class":61,"line":423},17,[59,425,214],{"class":69},[59,427,160],{"class":159},[59,429,164],{"class":163},[59,431,167],{"class":69},[59,433,434],{"class":65},"\"book-card__actions\"",[59,436,173],{"class":69},[59,438,440,442,444,446,448,451,454,456],{"class":61,"line":439},18,[59,441,273],{"class":69},[59,443,276],{"class":159},[59,445,164],{"class":163},[59,447,167],{"class":69},[59,449,450],{"class":65},"\"book-card__action--primary\"",[59,452,453],{"class":69},">Add to Cart\u003C\u002F",[59,455,276],{"class":159},[59,457,173],{"class":69},[59,459,461,463,465,467,469,472,475,477],{"class":61,"line":460},19,[59,462,273],{"class":69},[59,464,276],{"class":159},[59,466,164],{"class":163},[59,468,167],{"class":69},[59,470,471],{"class":65},"\"book-card__action--secondary\"",[59,473,474],{"class":69},">MORe\u003C\u002F",[59,476,276],{"class":159},[59,478,173],{"class":69},[59,480,482,484,486],{"class":61,"line":481},20,[59,483,296],{"class":69},[59,485,160],{"class":159},[59,487,173],{"class":69},[59,489,491,493,495],{"class":61,"line":490},21,[59,492,306],{"class":69},[59,494,160],{"class":159},[59,496,173],{"class":69},[59,498,500,502,504],{"class":61,"line":499},22,[59,501,316],{"class":69},[59,503,160],{"class":159},[59,505,173],{"class":69},[10,507,508],{},"此时我们发现，用相似的结构可以描述两个组件，且两个组件的风格大部分相同。此时开发者有两个选项：要么复制几乎完全相同的两份 CSS 并且违反 DRY 原则，要么将两个组件中的相同样式「抽象」成更通用的类。随着这种情况越来越多，我们的“通用类”也会越来越多，逐渐演变成这样：",[50,510,512],{"className":52,"code":511,"language":54,"meta":55,"style":55},".box {}\n.has-ribbon {}\n.shadow {}\n",[45,513,514,521,528],{"__ignoreMap":55},[59,515,516,519],{"class":61,"line":62},[59,517,518],{"class":65},".box",[59,520,70],{"class":69},[59,522,523,526],{"class":61,"line":73},[59,524,525],{"class":65},".has-ribbon",[59,527,70],{"class":69},[59,529,530,533],{"class":61,"line":81},[59,531,532],{"class":65},".shadow",[59,534,70],{"class":69},[50,536,538],{"className":147,"code":537,"language":149,"meta":55,"style":55},"\u003Cdiv class=\"box shadow author-card\">\u003C\u002Fdiv>\n\u003Cdiv class=\"box has-ribbon shadow book-card\">\u003C\u002Fdiv>\n",[45,539,540,559],{"__ignoreMap":55},[59,541,542,544,546,548,550,553,555,557],{"class":61,"line":62},[59,543,156],{"class":69},[59,545,160],{"class":159},[59,547,164],{"class":163},[59,549,167],{"class":69},[59,551,552],{"class":65},"\"box shadow author-card\"",[59,554,190],{"class":69},[59,556,160],{"class":159},[59,558,173],{"class":69},[59,560,561,563,565,567,569,572,574,576],{"class":61,"line":73},[59,562,156],{"class":69},[59,564,160],{"class":159},[59,566,164],{"class":163},[59,568,167],{"class":69},[59,570,571],{"class":65},"\"box has-ribbon shadow book-card\"",[59,573,190],{"class":69},[59,575,160],{"class":159},[59,577,173],{"class":69},[10,579,580,581,583,584,586],{},"此时，我们又破坏了 BEM 的语义体系：我们创建了",[45,582,518],{},"、",[45,585,532],{},"等 utility class，而这本质上正是 Tailwind 之类的原子化框架诞生的意义。",[111,588,590],{"id":589},"变体variants爆炸","变体（variants）爆炸",[10,592,593],{},"在上面的例子中，卡片组件存在多种「变体」：横版、竖版、促销态、常规态等等。对应的 CSS 类名在 BEM 的规范下应当是：",[50,595,597],{"className":52,"code":596,"language":54,"meta":55,"style":55},".card--horizontal {}\n.card--vertical {}\n.card--promotional {}\n.card--normal {}\n",[45,598,599,606,613,620],{"__ignoreMap":55},[59,600,601,604],{"class":61,"line":62},[59,602,603],{"class":65},".card--horizontal",[59,605,70],{"class":69},[59,607,608,611],{"class":61,"line":73},[59,609,610],{"class":65},".card--vertical",[59,612,70],{"class":69},[59,614,615,618],{"class":61,"line":81},[59,616,617],{"class":65},".card--promotional",[59,619,70],{"class":69},[59,621,622,625],{"class":61,"line":89},[59,623,624],{"class":65},".card--normal",[59,626,70],{"class":69},[10,628,629],{},"这些变体之间可以组合，例如一个卡片可以既是竖版也是促销态。当多个 modifier 同时存在时，样式可能存在覆盖。",[50,631,633],{"className":52,"code":632,"language":54,"meta":55,"style":55},".card--vertical.card--promotional {}\n",[45,634,635],{"__ignoreMap":55},[59,636,637,640],{"class":61,"line":62},[59,638,639],{"class":65},".card--vertical.card--promotional",[59,641,70],{"class":69},[10,643,644],{},"随着变体的数量越来越多，可能的 modifiers 的组合也越来越多。此时 CSS 的规则也越来越复杂。BEM 指出 modifier 不应当互相依赖，但在现实世界中，一个最简单的组件都可能包含多个同时作用的 modifier。",[10,646,647],{},"更进一步地，考虑竖版卡片的样式。当卡片的布局出现改变，不可避免地将会影响内部元素的样式：MORE 按钮的宽度占据了 100% 容易宽度、介绍文字移动到了下方，且卡片标题和 MORE 按钮呈现上下两端布局。于是 CSS 结构中，element 的样式开始受到 modifier 的影响，CSS 的结构出现了层级耦合。",[50,649,651],{"className":52,"code":650,"language":54,"meta":55,"style":55},".card--vertical .card__title {}\n.card--vertical .card__intro {}\n.card--vertical .card__action--primary {}\n",[45,652,653,662,671],{"__ignoreMap":55},[59,654,655,657,660],{"class":61,"line":62},[59,656,610],{"class":65},[59,658,659],{"class":65}," .card__title",[59,661,70],{"class":69},[59,663,664,666,669],{"class":61,"line":73},[59,665,610],{"class":65},[59,667,668],{"class":65}," .card__intro",[59,670,70],{"class":69},[59,672,673,675,678],{"class":61,"line":81},[59,674,610],{"class":65},[59,676,677],{"class":65}," .card__action--primary",[59,679,70],{"class":69},[10,681,682],{},"此时的 CSS 文件中不可避免地会出现大量的样式覆盖，CSS 声明的数量将迅速增长。",[111,684,685],{"id":685},"重复的样式与维护困难",[10,687,688,689,692],{},"在践行 BEM 的过程中，我们始终在",[106,690,691],{},"创造概念","。随着系统规模的进一步扩大，这又会暴露出其他问题：",[10,694,695,696,699,700,702,703,706],{},"其一是修改样式影响面难以确定，CSS 认知成本不断上升。",[45,697,698],{},".card__title","类的样式规则可能有多个组件共同使用，开发者缺少静态分析工具来保障自己的修改或删除始终是安全的。相比于在 HTML 中使用",[45,701,698],{},"，开发者们会倾向于创建自己的",[45,704,705],{},".custom-card__title","来避免自己的代码受到这个问题的影响。于是在这种情况下，CSS 规则只增不减，概念越来越多，起名的负担越来越重，维护性越来越差。",[10,708,709,710,713],{},"其次是重复样式的问题。随着类名越来越长、越来越多，相当多的规则可能只是实现了诸如",[45,711,712],{},"margin-left: 8px","之类的简单样式。选择器的长度甚至远远大于样式规则，大量重复的样式代码在不同的组件中反复出现，对开发者的开发体验和构建产物都造成了影响。",[111,715,716],{"id":716},"组件化的发展",[10,718,719],{},"BEM 诞生于静态页面时代，它在大型项目中反映出来的问题，归根结底是因为 BEM 对关注点分离的坚守。这一点在组件化时代显得与我们主流的认识「格格不入」。当我们把 UI 拆分为可复用的组件时，HTML 和 CSS 其实共同构成了一个组件的外观。强行将它们按照文件类型进行分离反而增加了维护负担。",[10,721,722],{},"在 Vue.js 的文档中，有这样一句话：",[724,725,726,729],"blockquote",{},[10,727,728],{},"separation of concerns is not equal to the separation of file types. The ultimate goal of engineering principles is to improve the maintainability of codebases.",[10,730,731],{},"关注点并不按照文件类型分离。工程化的终极目标应当是提升代码库的可维护性。",[10,733,734],{},"在规范的要求下，我们编写了一个 BEM 风格的 HTML；紧接着，我们转到 CSS 文件中，又为相同的 DOM 结构编写样式。实际上这两者从未真正地分离，我们只是在不同文件中以不同方式描述了相同的树。面对组件化的发展，前端 CSS 迫切需要一种新的指导思想。",[34,736,738],{"id":737},"utility-first原子化-css-的崛起","Utility-first：原子化 CSS 的崛起",[10,740,741],{},"原子化 CSS（Atomic CSS）是指将样式拆分为最小的、不可再分的单元，使每个简短的类名只对应一条属性。",[50,743,745],{"className":147,"code":744,"language":149,"meta":55,"style":55},"\u003C!-- book -->\n\u003Cdiv class=\"flex gap-4\">\n  \u003Cimg src=\"...\" class=\"w-1\u002F3\" \u002F>\n  \u003Cdiv class=\"w-2\u002F3\">\n    \u003Cdiv class=\"text-lg text-slate-8 line-clamp-2\">And Then There Were None\u003C\u002Fdiv>\n    \u003Cdiv class=\"text-slate-5 mt-4\">...\u003C\u002Fdiv>\n    \u003Cbutton class=\"bg-slate-800 text-white uppercase mt-4\">MORE\u003C\u002Fbutton>\n  \u003C\u002Fdiv>\n\u003C\u002Fdiv>\n",[45,746,747,753,768,792,807,826,845,864,872],{"__ignoreMap":55},[59,748,749],{"class":61,"line":62},[59,750,752],{"class":751},"sy89t","\u003C!-- book -->\n",[59,754,755,757,759,761,763,766],{"class":61,"line":73},[59,756,156],{"class":69},[59,758,160],{"class":159},[59,760,164],{"class":163},[59,762,167],{"class":69},[59,764,765],{"class":65},"\"flex gap-4\"",[59,767,173],{"class":69},[59,769,770,772,774,777,779,782,784,786,789],{"class":61,"line":81},[59,771,178],{"class":69},[59,773,138],{"class":159},[59,775,776],{"class":163}," src",[59,778,167],{"class":69},[59,780,781],{"class":65},"\"...\"",[59,783,164],{"class":163},[59,785,167],{"class":69},[59,787,788],{"class":65},"\"w-1\u002F3\"",[59,790,791],{"class":69}," \u002F>\n",[59,793,794,796,798,800,802,805],{"class":61,"line":89},[59,795,178],{"class":69},[59,797,160],{"class":159},[59,799,164],{"class":163},[59,801,167],{"class":69},[59,803,804],{"class":65},"\"w-2\u002F3\"",[59,806,173],{"class":69},[59,808,809,811,813,815,817,820,822,824],{"class":61,"line":233},[59,810,214],{"class":69},[59,812,160],{"class":159},[59,814,164],{"class":163},[59,816,167],{"class":69},[59,818,819],{"class":65},"\"text-lg text-slate-8 line-clamp-2\"",[59,821,396],{"class":69},[59,823,160],{"class":159},[59,825,173],{"class":69},[59,827,828,830,832,834,836,839,841,843],{"class":61,"line":254},[59,829,214],{"class":69},[59,831,160],{"class":159},[59,833,164],{"class":163},[59,835,167],{"class":69},[59,837,838],{"class":65},"\"text-slate-5 mt-4\"",[59,840,247],{"class":69},[59,842,160],{"class":159},[59,844,173],{"class":69},[59,846,847,849,851,853,855,858,860,862],{"class":61,"line":270},[59,848,214],{"class":69},[59,850,276],{"class":159},[59,852,164],{"class":163},[59,854,167],{"class":69},[59,856,857],{"class":65},"\"bg-slate-800 text-white uppercase mt-4\"",[59,859,286],{"class":69},[59,861,276],{"class":159},[59,863,173],{"class":69},[59,865,866,868,870],{"class":61,"line":293},[59,867,306],{"class":69},[59,869,160],{"class":159},[59,871,173],{"class":69},[59,873,874,876,878],{"class":61,"line":303},[59,875,316],{"class":69},[59,877,160],{"class":159},[59,879,173],{"class":69},[10,881,882],{},"对比两种范式，BEM 整体建立在「约定」的基础上，而原子化 CSS 的理念则是组合优先。通过组合已有的类名实现 UI 的构建，在此过程中不会创造新的概念，也避免了无意义的抽象。这种思想也被叫做 utility-first。",[10,884,885],{},"原子化 CSS 被大规模使用，背后有几条工程驱动的原因：",[887,888,889,896],"ul",{},[890,891,892,895],"li",{},[106,893,894],{},"构建工具的成熟","。Bootstrap 的 CSS 中层含有非常少量的 utility class，因为完全通过类名来还原几乎所有常见样式声明所导致的 CSS 体积必然是巨大的。只有在通过使用 WebPack、Rollup、PostCSS 等构建工具在构建阶段进行分析、扫描与优化，使得未使用的样式可以被剔除出构建产物，原子化 CSS 的方案才能真正发展起来。",[890,897,898,899,902],{},"使得原子化 CSS 方案脱颖而出的另一点，就是",[106,900,901],{},"现代产品越来越依赖于设计系统","。固有的空间尺度、色板与排版尺度使得「一组可复用原子」变得非常有价值。Tailwind 所提供的预设设计令牌可以作为设计系统的基底，看似限制了开发者的自由度，但在产品的宏观层面保证了设计的一致性，降低了决策成本。",[34,904,905],{"id":905},"重塑工程化边界",[10,907,908],{},"从 BEM 到 Tailwind 的演进，不仅是一种 CSS 编写方式和思路的转变，也触及了前端工程化的核心问题：我们如何组织代码，以平衡开发效率、可维护性与可扩展性。在实用主义的驱动下，Tailwind 越来越受欢迎，工程化边界也得到了重新定义。",[10,910,911,912,915],{},"在 Tailwind 之前，样式库的维护——包括命名规范、样式复用和样式覆盖策略——是前端工程团队的核心任务。在 utility-first 指导的设计与开发中，这部分工作被抽象成设计 tokens 与类集，维护在诸如",[45,913,914],{},"tailwind.config.js","之类的配置文件里。这些 tokens 天然易于导出，且可与设计软件同步，使得跨端一致性更容易落地，也在某种程度上降低视觉回归的难度。",[10,917,918,919,922,923,928,929,931],{},"当然，使用 Tailwind 后，开发者也会遇到各种挑战与新的问题。借助原子 CSS 框架预定义的 tokens，开发者更多参与视觉的实现，设计稿与代码之间的反馈回路变短；但这也要求开发者必须具备 token 意识，避免随意增加 token。更重要的是，由于用户界面是组合而成的，一个原子 CSS 类的辐射面可能很大，因此在设计规范变更时，需要团队调整至更细致和严格的审查流程。最后，过度的抽象也依然是潜在的风险，如果开发者滥用",[45,920,921],{},"@apply","将类名组合进行「封装」，就不知不觉走了 BEM 的老路（事实上，就连 Tailwind 的创造者 Adam Wathan 本人也",[14,924,927],{"href":925,"rel":926},"https:\u002F\u002Fx.com\u002Fadamwathan\u002Fstatus\u002F1226511611592085504?lang=en",[18],"极其反对","使用",[45,930,921],{},"）。",[10,933,934],{},"从 BEM 到 Tailwind 的演进，核心并非“语义好不好”或“类名美学”的胜负，而是前端工程化的一系列决策：谁负责定义视觉规则、这些规则在何时被计算、以及团队如何在速度与一致性之间取得平衡。BEM 把复杂性放在明确的约定与语义层上，而 Tailwind 把复杂性放在构建时与 token 管理上，并把组合能力交给最终代码作者。",[10,936,937],{},"现实世界里的最佳实践往往是混合的：让设计 tokens 成为稳定的单一事实源（source of truth），在组件边界上保留语义化 API，同时在组件内部使用实用类获得开发速度与一致性。这种折衷既能保留 BEM 在协作与可读性上的价值，也能享受 Tailwind 在工程效率与构建优化上的好处。",[939,940,941],"style",{},"html pre.shiki code .sb16X, html code.shiki .sb16X{--shiki-default:#4BF3C8}html pre.shiki code .sLo_3, html code.shiki .sLo_3{--shiki-default:#EEF0F9}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sqF6k, html code.shiki .sqF6k{--shiki-default:#54B9FF}html pre.shiki code .s8HWQ, html code.shiki .s8HWQ{--shiki-default:#ACAFFF}html pre.shiki code .sy89t, html code.shiki .sy89t{--shiki-default:#EEF0F98F;--shiki-default-font-style:italic}",{"title":55,"searchDepth":73,"depth":73,"links":943},[944,945,951,952],{"id":36,"depth":73,"text":37},{"id":100,"depth":73,"text":101,"children":946},[947,948,949,950],{"id":113,"depth":81,"text":114},{"id":589,"depth":81,"text":590},{"id":685,"depth":81,"text":685},{"id":716,"depth":81,"text":716},{"id":737,"depth":73,"text":738},{"id":905,"depth":73,"text":905},"2023.02.16","无论 web 开发框架如何层出不穷、日新月异，浏览器始终只能识别 HTML、CSS 和 JS。前端工程师们对于 CSS 的讨论逐渐从「选择器优雅」「语义化类名」迁移到了「可组合性」「构建时优化」和「工程效率」。在过去 BEM（Block-Element-Modifier）代表了对中大型协作项目中可维护性的工程化回答；但在近年来，以 Tailwind 为代表的 utility-first 工具异军突起，Tailwind 也成为了社区中最受欢迎的 CSS 技术之一。两者的策略和风格截然不同，但随着越来越多的项目使用了 Tailwind，我们不得不认同它正在重塑 CSS 的架构。","md","从 BEM 到 Tailwind，实用主义是如何重塑 CSS 的",{},"\u002Fwriting\u002Fcss-bem-or-atom",{"title":5,"description":954},"writing\u002Fcss-bem-or-atom",[962,963],"CSS","engineering","uhAEivixO-vJBfeCM_QzZZHdQIFAIVgtrtQbmg6xJys",1774950409696]