[{"data":1,"prerenderedAt":1357},["ShallowReactive",2],{"\u002Fposts\u002Fwhy-not-use-structured-styles":3,"surround:\u002Fposts\u002Fwhy-not-use-structured-styles":1351},{"id":4,"title":5,"body":6,"createdAt":1344,"description":16,"extension":1345,"meta":1346,"navigation":1232,"path":1347,"seo":1348,"stem":1349,"__hash__":1350},"posts\u002Fposts\u002F2025\u002Fwhy-not-use-structured-styles.md","为什么我不推荐你使用“结构化样式”",{"type":7,"value":8,"toc":1337},"minimark",[9,13,17,81,84,87,92,95,98,159,162,170,264,271,353,356,359,362,372,375,474,477,492,496,499,596,602,605,731,738,745,838,860,864,871,924,927,978,985,993,996,1002,1006,1009,1064,1071,1074,1129,1132,1136,1139,1142,1156,1162,1164,1167,1315,1326,1329,1331,1334],[10,11,5],"h1",{"id":12},"为什么我不推荐你使用结构化样式",[14,15,16],"p",{},"在 Web 开发中，结构、样式、逻辑代码的组织关系是一个老生常谈的问题。当我们普遍认为“组件化”是解决工程复杂度的最佳实践时，很多人会将结构-样式-逻辑的内聚和耦合混淆起来。以样式为例，一种常见的情况是这样的：",[18,19,24],"pre",{"className":20,"code":21,"language":22,"meta":23,"style":23},"language-css shiki shiki-themes material-theme-lighter material-theme material-theme-palenight",".list {\n  \u002F* ... *\u002F\n}\n.list .list-item {\n  \u002F* ... *\u002F\n}\n","css","",[25,26,27,43,50,56,71,76],"code",{"__ignoreMap":23},[28,29,32,36,40],"span",{"class":30,"line":31},"line",1,[28,33,35],{"class":34},"sMK4o",".",[28,37,39],{"class":38},"sBMFI","list",[28,41,42],{"class":34}," {\n",[28,44,46],{"class":30,"line":45},2,[28,47,49],{"class":48},"sHwdD","  \u002F* ... *\u002F\n",[28,51,53],{"class":30,"line":52},3,[28,54,55],{"class":34},"}\n",[28,57,59,61,63,66,69],{"class":30,"line":58},4,[28,60,35],{"class":34},[28,62,39],{"class":38},[28,64,65],{"class":34}," .",[28,67,68],{"class":38},"list-item",[28,70,42],{"class":34},[28,72,74],{"class":30,"line":73},5,[28,75,49],{"class":48},[28,77,79],{"class":30,"line":78},6,[28,80,55],{"class":34},[14,82,83],{},"当然这只是一个使用基本 CSS 的例子。如果你使用 Less\u002FSCSS\u002FStylus，可以构造出更加“结构化”的代码，但它们本质上并没有不同。",[14,85,86],{},"今天想要和大家一起讨论的就是，为什么我觉得这是一种不好的设计，以及在当下，如何解决复杂样式的抽象问题。",[88,89,91],"h2",{"id":90},"is-a-white-horse-foremost-a-horse-or-a-white-thing","白马首先是马，还是白色的东西？",[14,93,94],{},"这里请让我举一个很常见的例子。为了更好地表达现代工程中可能出现的问题，下面我会使用 SCSS 作为示例。",[14,96,97],{},"假设我们的组件是一个卡片，卡片中有一个徽标。我们很容易写出这样的实现：",[18,99,103],{"className":100,"code":101,"language":102,"meta":23,"style":23},"language-scss shiki shiki-themes material-theme-lighter material-theme material-theme-palenight",".card {\n  display: flex;\n}\n.badge {\n  appearance: none;\n}\n","scss",[25,104,105,114,130,134,143,155],{"__ignoreMap":23},[28,106,107,109,112],{"class":30,"line":31},[28,108,35],{"class":34},[28,110,111],{"class":38},"card",[28,113,42],{"class":34},[28,115,116,120,123,127],{"class":30,"line":45},[28,117,119],{"class":118},"sqsOY","  display",[28,121,122],{"class":34},":",[28,124,126],{"class":125},"sTEyZ"," flex",[28,128,129],{"class":34},";\n",[28,131,132],{"class":30,"line":52},[28,133,55],{"class":34},[28,135,136,138,141],{"class":30,"line":58},[28,137,35],{"class":34},[28,139,140],{"class":38},"badge",[28,142,42],{"class":34},[28,144,145,148,150,153],{"class":30,"line":73},[28,146,147],{"class":118},"  appearance",[28,149,122],{"class":34},[28,151,152],{"class":125}," none",[28,154,129],{"class":34},[28,156,157],{"class":30,"line":78},[28,158,55],{"class":34},[14,160,161],{},"现在我们需要实现一个特性：当卡片处于禁用状态时，徽标的透明度将会提高。接下来，我们有两种选择：",[14,163,164,165,169],{},"第一种是：",[166,167,168],"strong",{},"卡片的禁用状态与卡片直接相关","，因此这个需求应该是：",[18,171,174],{"className":172,"code":173,"language":102,"meta":23,"style":23},"language-scss shiki shiki-themes material-theme-lighter material-theme material-theme-palenight has-diff",".card {\n  display: flex;\n  &.is-disabled .badge { \u002F\u002F [!code ++]\n    opacity: 0.5; \u002F\u002F [!code ++]\n  } \u002F\u002F [!code ++]\n}\n.badge {\n  appearance: none;\n}\n",[25,175,176,184,194,214,229,235,239,248,259],{"__ignoreMap":23},[28,177,178,180,182],{"class":30,"line":31},[28,179,35],{"class":34},[28,181,111],{"class":38},[28,183,42],{"class":34},[28,185,186,188,190,192],{"class":30,"line":45},[28,187,119],{"class":118},[28,189,122],{"class":34},[28,191,126],{"class":125},[28,193,129],{"class":34},[28,195,199,202,204,207,209,211],{"class":196,"line":52},[30,197,198],"diff","add",[28,200,201],{"class":38},"  &",[28,203,35],{"class":34},[28,205,206],{"class":38},"is-disabled",[28,208,65],{"class":34},[28,210,140],{"class":38},[28,212,213],{"class":34}," {",[28,215,217,220,222,226],{"class":216,"line":58},[30,197,198],[28,218,219],{"class":118},"    opacity",[28,221,122],{"class":34},[28,223,225],{"class":224},"sbssI"," 0.5",[28,227,228],{"class":34},";",[28,230,232],{"class":231,"line":73},[30,197,198],[28,233,234],{"class":34},"  }",[28,236,237],{"class":30,"line":78},[28,238,55],{"class":34},[28,240,242,244,246],{"class":30,"line":241},7,[28,243,35],{"class":34},[28,245,140],{"class":38},[28,247,42],{"class":34},[28,249,251,253,255,257],{"class":30,"line":250},8,[28,252,147],{"class":118},[28,254,122],{"class":34},[28,256,152],{"class":125},[28,258,129],{"class":34},[28,260,262],{"class":30,"line":261},9,[28,263,55],{"class":34},[14,265,266,267,270],{},"第二种是：",[166,268,269],{},"尽管禁用是卡片的状态，但本质上我们是在实现一个徽标的变体","。因此应该实现为：",[18,272,274],{"className":172,"code":273,"language":102,"meta":23,"style":23},".card {\n  display: flex;\n}\n.badge {\n  appearance: none;\n  .card.is-disabled & { \u002F\u002F [!code ++]\n    opacity: 0.5; \u002F\u002F [!code ++]\n  } \u002F\u002F [!code ++]\n}\n",[25,275,276,284,294,298,306,316,333,344,349],{"__ignoreMap":23},[28,277,278,280,282],{"class":30,"line":31},[28,279,35],{"class":34},[28,281,111],{"class":38},[28,283,42],{"class":34},[28,285,286,288,290,292],{"class":30,"line":45},[28,287,119],{"class":118},[28,289,122],{"class":34},[28,291,126],{"class":125},[28,293,129],{"class":34},[28,295,296],{"class":30,"line":52},[28,297,55],{"class":34},[28,299,300,302,304],{"class":30,"line":58},[28,301,35],{"class":34},[28,303,140],{"class":38},[28,305,42],{"class":34},[28,307,308,310,312,314],{"class":30,"line":73},[28,309,147],{"class":118},[28,311,122],{"class":34},[28,313,152],{"class":125},[28,315,129],{"class":34},[28,317,319,322,324,326,328,331],{"class":318,"line":78},[30,197,198],[28,320,321],{"class":34},"  .",[28,323,111],{"class":38},[28,325,35],{"class":34},[28,327,206],{"class":38},[28,329,330],{"class":38}," &",[28,332,213],{"class":34},[28,334,336,338,340,342],{"class":335,"line":241},[30,197,198],[28,337,219],{"class":118},[28,339,122],{"class":34},[28,341,225],{"class":224},[28,343,228],{"class":34},[28,345,347],{"class":346,"line":250},[30,197,198],[28,348,234],{"class":34},[28,350,351],{"class":30,"line":261},[28,352,55],{"class":34},[14,354,355],{},"你认为比较合理的是哪一种呢？",[357,358],"hr",{},[14,360,361],{},"如果这个例子不那么直观，我们可以将它扩展一下。在一个复杂的项目中，我们可以给出更多条件：",[363,364,365,369],"ul",{},[366,367,368],"li",{},"徽标本身就有透明度设置",[366,370,371],{},"卡片中除了徽标，还有更多结构",[14,373,374],{},"于是这个代码可能是：",[18,376,378],{"className":100,"code":377,"language":102,"meta":23,"style":23},".card {\n  display: flex;\n}\n.header {\n  \u002F* ... *\u002F\n}\n.body {\n  \u002F* ... *\u002F\n}\n.badge {\n  appearance: none;\n  opacity: 0.8;\n}\n",[25,379,380,388,398,402,411,415,419,428,432,436,445,456,469],{"__ignoreMap":23},[28,381,382,384,386],{"class":30,"line":31},[28,383,35],{"class":34},[28,385,111],{"class":38},[28,387,42],{"class":34},[28,389,390,392,394,396],{"class":30,"line":45},[28,391,119],{"class":118},[28,393,122],{"class":34},[28,395,126],{"class":125},[28,397,129],{"class":34},[28,399,400],{"class":30,"line":52},[28,401,55],{"class":34},[28,403,404,406,409],{"class":30,"line":58},[28,405,35],{"class":34},[28,407,408],{"class":38},"header",[28,410,42],{"class":34},[28,412,413],{"class":30,"line":73},[28,414,49],{"class":48},[28,416,417],{"class":30,"line":78},[28,418,55],{"class":34},[28,420,421,423,426],{"class":30,"line":241},[28,422,35],{"class":34},[28,424,425],{"class":38},"body",[28,427,42],{"class":34},[28,429,430],{"class":30,"line":250},[28,431,49],{"class":48},[28,433,434],{"class":30,"line":261},[28,435,55],{"class":34},[28,437,439,441,443],{"class":30,"line":438},10,[28,440,35],{"class":34},[28,442,140],{"class":38},[28,444,42],{"class":34},[28,446,448,450,452,454],{"class":30,"line":447},11,[28,449,147],{"class":118},[28,451,122],{"class":34},[28,453,152],{"class":125},[28,455,129],{"class":34},[28,457,459,462,464,467],{"class":30,"line":458},12,[28,460,461],{"class":118},"  opacity",[28,463,122],{"class":34},[28,465,466],{"class":224}," 0.8",[28,468,129],{"class":34},[28,470,472],{"class":30,"line":471},13,[28,473,55],{"class":34},[14,475,476],{},"不装了，我摊牌了，其实我就是【第二种方式】的鼓吹者。因为只有把同一个元素在不同条件下的样式都放在一起，才能确保该元素的样式能够得到统一维护。",[14,478,479,480,483,484,487,488,491],{},"本质上，",[25,481,482],{},".card.is-disabled &"," 相当于是 ",[25,485,486],{},".badge"," 的“Props”，我们总是应该尽可能把针对 Props 的逻辑放在组件内部，而不是在父组件完成处理再传入一个大的 ",[25,489,490],{},"children"," 进来（你会这么做吗？）。",[88,493,495],{"id":494},"you-are-first-yourself-and-then-others","你首先是你自己，然后才是……",[14,497,498],{},"这个例子和我说的“结构化样式”有什么关系呢？当然有。我在示例中使用的样式都是“扁平化”的，假设我们采用嵌套的形式编写，它可能会是：",[18,500,502],{"className":100,"code":501,"language":102,"meta":23,"style":23},".card {\n  display: flex;\n  .header {\n    \u002F* ... *\u002F\n    .badge {\n      appearance: none;\n      opacity: 0.8;\n    }\n  }\n  .body {\n    \u002F* ... *\u002F\n  }\n}\n",[25,503,504,512,522,530,535,544,555,566,571,576,584,588,592],{"__ignoreMap":23},[28,505,506,508,510],{"class":30,"line":31},[28,507,35],{"class":34},[28,509,111],{"class":38},[28,511,42],{"class":34},[28,513,514,516,518,520],{"class":30,"line":45},[28,515,119],{"class":118},[28,517,122],{"class":34},[28,519,126],{"class":125},[28,521,129],{"class":34},[28,523,524,526,528],{"class":30,"line":52},[28,525,321],{"class":34},[28,527,408],{"class":38},[28,529,42],{"class":34},[28,531,532],{"class":30,"line":58},[28,533,534],{"class":48},"    \u002F* ... *\u002F\n",[28,536,537,540,542],{"class":30,"line":73},[28,538,539],{"class":34},"    .",[28,541,140],{"class":38},[28,543,42],{"class":34},[28,545,546,549,551,553],{"class":30,"line":78},[28,547,548],{"class":118},"      appearance",[28,550,122],{"class":34},[28,552,152],{"class":125},[28,554,129],{"class":34},[28,556,557,560,562,564],{"class":30,"line":241},[28,558,559],{"class":118},"      opacity",[28,561,122],{"class":34},[28,563,466],{"class":224},[28,565,129],{"class":34},[28,567,568],{"class":30,"line":250},[28,569,570],{"class":34},"    }\n",[28,572,573],{"class":30,"line":261},[28,574,575],{"class":34},"  }\n",[28,577,578,580,582],{"class":30,"line":438},[28,579,321],{"class":34},[28,581,425],{"class":38},[28,583,42],{"class":34},[28,585,586],{"class":30,"line":447},[28,587,534],{"class":48},[28,589,590],{"class":30,"line":458},[28,591,575],{"class":34},[28,593,594],{"class":30,"line":471},[28,595,55],{"class":34},[14,597,598,599,601],{},"那么此时，我应该如何将 ",[25,600,482],{}," 条件添加到已有的样式里呢？",[14,603,604],{},"当然，你可以觉得，既然这样，那在这种条件下，我们就不要使用层层嵌套的形式了嘛，直接把它挪到外面，这样我们也不用纠结前面讨论的问题了！",[18,606,608],{"className":172,"code":607,"language":102,"meta":23,"style":23},".card {\n  display: flex;\n  .header {\n    \u002F* ... *\u002F\n  }\n  .badge {\n    appearance: none;\n    opacity: 0.8;\n  }\n  &.is-disabled .badge {  \u002F\u002F [!code ++]\n    opacity: 0.5;  \u002F\u002F [!code ++]\n  }  \u002F\u002F [!code ++]\n  .body {\n    \u002F* ... *\u002F\n  }\n}\n",[25,609,610,618,628,636,640,644,652,663,673,677,692,703,708,716,721,726],{"__ignoreMap":23},[28,611,612,614,616],{"class":30,"line":31},[28,613,35],{"class":34},[28,615,111],{"class":38},[28,617,42],{"class":34},[28,619,620,622,624,626],{"class":30,"line":45},[28,621,119],{"class":118},[28,623,122],{"class":34},[28,625,126],{"class":125},[28,627,129],{"class":34},[28,629,630,632,634],{"class":30,"line":52},[28,631,321],{"class":34},[28,633,408],{"class":38},[28,635,42],{"class":34},[28,637,638],{"class":30,"line":58},[28,639,534],{"class":48},[28,641,642],{"class":30,"line":73},[28,643,575],{"class":34},[28,645,646,648,650],{"class":30,"line":78},[28,647,321],{"class":34},[28,649,140],{"class":38},[28,651,42],{"class":34},[28,653,654,657,659,661],{"class":30,"line":241},[28,655,656],{"class":118},"    appearance",[28,658,122],{"class":34},[28,660,152],{"class":125},[28,662,129],{"class":34},[28,664,665,667,669,671],{"class":30,"line":250},[28,666,219],{"class":118},[28,668,122],{"class":34},[28,670,466],{"class":224},[28,672,129],{"class":34},[28,674,675],{"class":30,"line":261},[28,676,575],{"class":34},[28,678,680,682,684,686,688,690],{"class":679,"line":438},[30,197,198],[28,681,201],{"class":38},[28,683,35],{"class":34},[28,685,206],{"class":38},[28,687,65],{"class":34},[28,689,140],{"class":38},[28,691,213],{"class":34},[28,693,695,697,699,701],{"class":694,"line":447},[30,197,198],[28,696,219],{"class":118},[28,698,122],{"class":34},[28,700,225],{"class":224},[28,702,228],{"class":34},[28,704,706],{"class":705,"line":458},[30,197,198],[28,707,234],{"class":34},[28,709,710,712,714],{"class":30,"line":471},[28,711,321],{"class":34},[28,713,425],{"class":38},[28,715,42],{"class":34},[28,717,719],{"class":30,"line":718},14,[28,720,534],{"class":48},[28,722,724],{"class":30,"line":723},15,[28,725,575],{"class":34},[28,727,729],{"class":30,"line":728},16,[28,730,55],{"class":34},[14,732,733,734,737],{},"那么在这里，我的问题是：凭啥？为什么我需要为了增加一个变体，改变原有的样式结构，甚至",[166,735,736],{},"影响到样式的优先级","？",[14,739,740,741,744],{},"这里我想表达我的第二个观点：",[166,742,743],{},"我们总是应该保持工程样式的最简化","。这不仅是为了可读性，同时也是“面向结构的样式”和“面向元素的样式”之间的 PK。我的意思是，即使不使用嵌套，我也不会使用下面的写法：",[18,746,748],{"className":20,"code":747,"language":22,"meta":23,"style":23},".card {\n  \u002F* ... *\u002F\n}\n.card .header {\n  \u002F* ... *\u002F\n}\n.card .header .badge {\n  \u002F* ... *\u002F\n}\n.card.is-disabled .header .badge {\n  \u002F* ... *\u002F\n}\n",[25,749,750,758,762,766,778,782,786,802,806,810,830,834],{"__ignoreMap":23},[28,751,752,754,756],{"class":30,"line":31},[28,753,35],{"class":34},[28,755,111],{"class":38},[28,757,42],{"class":34},[28,759,760],{"class":30,"line":45},[28,761,49],{"class":48},[28,763,764],{"class":30,"line":52},[28,765,55],{"class":34},[28,767,768,770,772,774,776],{"class":30,"line":58},[28,769,35],{"class":34},[28,771,111],{"class":38},[28,773,65],{"class":34},[28,775,408],{"class":38},[28,777,42],{"class":34},[28,779,780],{"class":30,"line":73},[28,781,49],{"class":48},[28,783,784],{"class":30,"line":78},[28,785,55],{"class":34},[28,787,788,790,792,794,796,798,800],{"class":30,"line":241},[28,789,35],{"class":34},[28,791,111],{"class":38},[28,793,65],{"class":34},[28,795,408],{"class":38},[28,797,65],{"class":34},[28,799,140],{"class":38},[28,801,42],{"class":34},[28,803,804],{"class":30,"line":250},[28,805,49],{"class":48},[28,807,808],{"class":30,"line":261},[28,809,55],{"class":34},[28,811,812,814,816,818,820,822,824,826,828],{"class":30,"line":438},[28,813,35],{"class":34},[28,815,111],{"class":38},[28,817,35],{"class":34},[28,819,206],{"class":38},[28,821,65],{"class":34},[28,823,408],{"class":38},[28,825,65],{"class":34},[28,827,140],{"class":38},[28,829,42],{"class":34},[28,831,832],{"class":30,"line":447},[28,833,49],{"class":48},[28,835,836],{"class":30,"line":458},[28,837,55],{"class":34},[14,839,840,841,843,844,847,848,851,852,855,856,859],{},"在上面的这些例子里，",[25,842,486],{}," 不因 ",[25,845,846],{},".card"," 和 ",[25,849,850],{},".header"," 而存在，尽可能保持非结构化更有助于我们在原本的样式中进行状态派生，而无论状态改变发生在哪个元素上；更何况，简单的样式能够让我们尽可能避免为了解决样式冲突而产生的 ",[25,853,854],{},".badge.badge.badge"," 或者 ",[25,857,858],{},"!important","。",[88,861,863],{"id":862},"bem-是组织样式代码的方式吗","BEM 是组织样式代码的方式吗？",[14,865,866,867,870],{},"在上面的例子里，我始终没有聊到的一个原因是",[166,868,869],{},"打包体积","。一旦我们讨论到体积问题，反驳者会提出这样的东西：",[18,872,874],{"className":100,"code":873,"language":102,"meta":23,"style":23},".card {\n  &-header {\n    \u002F* ... *\u002F\n    &-badge {\n      \u002F* ... *\u002F\n    }\n  }\n}\n",[25,875,876,884,893,897,907,912,916,920],{"__ignoreMap":23},[28,877,878,880,882],{"class":30,"line":31},[28,879,35],{"class":34},[28,881,111],{"class":38},[28,883,42],{"class":34},[28,885,886,888,891],{"class":30,"line":45},[28,887,201],{"class":38},[28,889,890],{"class":34},"-header",[28,892,42],{"class":34},[28,894,895],{"class":30,"line":52},[28,896,534],{"class":48},[28,898,899,902,905],{"class":30,"line":58},[28,900,901],{"class":38},"    &",[28,903,904],{"class":34},"-badge",[28,906,42],{"class":34},[28,908,909],{"class":30,"line":73},[28,910,911],{"class":48},"      \u002F* ... *\u002F\n",[28,913,914],{"class":30,"line":78},[28,915,570],{"class":34},[28,917,918],{"class":30,"line":241},[28,919,575],{"class":34},[28,921,922],{"class":30,"line":250},[28,923,55],{"class":34},[14,925,926],{},"仿佛这样就用他们想要的样子实现了“扁平化”。从我们前面讨论的例子不难看出，这实际上和 class 嵌套并无二致。反对者可能会拿出 BEM 来反驳，并指出它是曾经很多主流组件库的“标准”：",[18,928,930],{"className":100,"code":929,"language":102,"meta":23,"style":23},".card {\n  &__header {\n    \u002F* ... *\u002F\n    &__badge {\n      \u002F* ... *\u002F\n    }\n  }\n}\n",[25,931,932,940,949,953,962,966,970,974],{"__ignoreMap":23},[28,933,934,936,938],{"class":30,"line":31},[28,935,35],{"class":34},[28,937,111],{"class":38},[28,939,42],{"class":34},[28,941,942,944,947],{"class":30,"line":45},[28,943,201],{"class":38},[28,945,946],{"class":34},"__header",[28,948,42],{"class":34},[28,950,951],{"class":30,"line":52},[28,952,534],{"class":48},[28,954,955,957,960],{"class":30,"line":58},[28,956,901],{"class":38},[28,958,959],{"class":34},"__badge",[28,961,42],{"class":34},[28,963,964],{"class":30,"line":73},[28,965,911],{"class":48},[28,967,968],{"class":30,"line":78},[28,969,570],{"class":34},[28,971,972],{"class":30,"line":241},[28,973,575],{"class":34},[28,975,976],{"class":30,"line":250},[28,977,55],{"class":34},[14,979,980,981,984],{},"而我想指出的则是：",[166,982,983],{},"BEM 不是用来组织样式代码的","！BEM 的唯一作用是“命名”。它诞生的时候之所以流行，是因为解决了这些问题：",[363,986,987,990],{},[366,988,989],{},"因为 HTML 的元素过于“裸”，我们不得不在很多层级上添加很多元素，于是结构里出现了很多 .container、.wrapper、.content、.body……",[366,991,992],{},"由于 CSS 的“Cascading”性质，我们总是希望尽量避免 class 名称的冲突，于是一种唯一性的生成逻辑可以最低成本帮助我们规避命名冲突",[14,994,995],{},"所有的这些优势，都和结构-样式的关联无关，BEM 最初就是为了解决命名问题。如今我们有 CSS Modules，有 Scoped CSS，有各种 CSS-in-JS 方案，命名的问题早就不存在了，BEM 现在主要的作用变成了降低命名成本，毕竟——",[997,998,999],"blockquote",{},[14,1000,1001],{},"There are only two hard things in Computer Science: cache invalidation and naming things. —— Phil Karlton",[88,1003,1005],{"id":1004},"预处理css-的遗产","预处理：CSS 的遗产",[14,1007,1008],{},"既然讨论到 BEM，我们不得不聊到 CSS Nesting。如果你还不知道，Chrome 120 也就是 2023 年开始，浏览器开始原生支持样式嵌套：",[18,1010,1012],{"className":20,"code":1011,"language":22,"meta":23,"style":23},".badge {\n  appearance: none;\n  .card.is-disabled & {\n    opacity: 0.5;\n  }\n}\n",[25,1013,1014,1022,1032,1046,1056,1060],{"__ignoreMap":23},[28,1015,1016,1018,1020],{"class":30,"line":31},[28,1017,35],{"class":34},[28,1019,140],{"class":38},[28,1021,42],{"class":34},[28,1023,1024,1026,1028,1030],{"class":30,"line":45},[28,1025,147],{"class":118},[28,1027,122],{"class":34},[28,1029,152],{"class":125},[28,1031,129],{"class":34},[28,1033,1034,1036,1038,1040,1042,1044],{"class":30,"line":52},[28,1035,321],{"class":34},[28,1037,111],{"class":38},[28,1039,35],{"class":34},[28,1041,206],{"class":38},[28,1043,330],{"class":38},[28,1045,42],{"class":34},[28,1047,1048,1050,1052,1054],{"class":30,"line":58},[28,1049,219],{"class":118},[28,1051,122],{"class":34},[28,1053,225],{"class":224},[28,1055,129],{"class":34},[28,1057,1058],{"class":30,"line":73},[28,1059,575],{"class":34},[28,1061,1062],{"class":30,"line":78},[28,1063,55],{"class":34},[14,1065,1066,1067,1070],{},"它等价于 ",[25,1068,1069],{},":is(.card.is-disabled) .badge","。使用 CSS Nesting 或许是一种历史趋势，因为 Sass 团队一直在致力于使 SCSS 的嵌套与 CSS 兼容，即使其再次成为 CSS 的超集。",[14,1072,1073],{},"但是如果你也想使用它，你必须知道：",[363,1075,1076,1115],{},[366,1077,1078,1079],{},"不是所有的 SCSS 嵌套语法在 CSS 中都支持，例如这个：\n",[18,1080,1082],{"className":20,"code":1081,"language":22,"meta":23,"style":23},".card {\n  &__badge {\n\u002F*^ 这是一个语法错误 *\u002F\n  }\n}\n",[25,1083,1084,1092,1102,1107,1111],{"__ignoreMap":23},[28,1085,1086,1088,1090],{"class":30,"line":31},[28,1087,35],{"class":34},[28,1089,111],{"class":38},[28,1091,42],{"class":34},[28,1093,1094,1096,1099],{"class":30,"line":45},[28,1095,201],{"class":38},[28,1097,1098],{"class":125},"__badge ",[28,1100,1101],{"class":34},"{\n",[28,1103,1104],{"class":30,"line":52},[28,1105,1106],{"class":48},"\u002F*^ 这是一个语法错误 *\u002F\n",[28,1108,1109],{"class":30,"line":58},[28,1110,575],{"class":34},[28,1112,1113],{"class":30,"line":73},[28,1114,55],{"class":34},[366,1116,1117,1118,859],{},"CSS Nesting 的优先级与 SCSS 有一点不同。这种不同会随着嵌套层数增加变得更加明显。关于这一点，我推荐你阅读 ",[1119,1120,1124,1125,1128],"a",{"href":1121,"rel":1122},"https:\u002F\u002Fblog.frankmtaylor.com\u002F2024\u002F07\u002F18\u002Fcss-nesting-the-is-pseudo-class-and-a-guide-to-panicking-about-sass\u002F",[1123],"nofollow","CSS Nesting, the ",[25,1126,1127],{},":is()"," pseudo-class, and a guide to panicking about Sass",[14,1130,1131],{},"因此，从向前兼容的角度，使用扁平化+非结构化 CSS 也是一种更好的选择。当然，扁平-嵌套的问题，已经有很多人讨论过了，这并不是我们今天要聊的重点；不过，使用扁平化样式会让我们自然而然使用“非结构化”样式，尽管这两者没有强烈的联系。",[88,1133,1135],{"id":1134},"tailwindcss-和内联样式","TailwindCSS 和内联样式",[14,1137,1138],{},"在这里我想讨论的另一个问题是，怎样脱离结构声明样式？",[14,1140,1141],{},"一个反直觉的事实是，“CSS 命名”本身就是在和结构耦合。当我们写下",[18,1143,1145],{"className":20,"code":1144,"language":22,"meta":23,"style":23},".card {}\n",[25,1146,1147],{"__ignoreMap":23},[28,1148,1149,1151,1153],{"class":30,"line":31},[28,1150,35],{"class":34},[28,1152,111],{"class":38},[28,1154,1155],{"class":34}," {}\n",[14,1157,1158,1159,859],{},"实际上就预设了这个元素的某种“结构”——它是一个卡片，所以它可能会有头部、主题内容，甚至带点交互。所以这里可能比较让人难以接受的事实是：真正的“非结构化样式”其实就是",[166,1160,1161],{},"内联样式",[357,1163],{},[14,1165,1166],{},"不论是 Styled Components 还是 CSS Modules，这些方案本质上都是在用“非结构化样式”代替“结构化样式”，实现的方式就是“避免 CSS 命名”。实际上他们都是在做这样的事情：",[18,1168,1172],{"className":1169,"code":1170,"language":1171,"meta":23,"style":23},"language-tsx shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const card = 'display: flex'\nconst header = 'font-weight: 500'\nconst badge = 'opacity: 0.7'\n\nreturn (\n  #div style={card}#\n    #div style={header}#\n      #div style={badge}##\u002Fdiv#\n    #\u002Fdiv#\n  #\u002Fdiv#\n)\n","tsx",[25,1173,1174,1196,1212,1228,1234,1243,1259,1272,1292,1301,1310],{"__ignoreMap":23},[28,1175,1176,1180,1183,1186,1189,1193],{"class":30,"line":31},[28,1177,1179],{"class":1178},"spNyl","const",[28,1181,1182],{"class":125}," card ",[28,1184,1185],{"class":34},"=",[28,1187,1188],{"class":34}," '",[28,1190,1192],{"class":1191},"sfazB","display: flex",[28,1194,1195],{"class":34},"'\n",[28,1197,1198,1200,1203,1205,1207,1210],{"class":30,"line":45},[28,1199,1179],{"class":1178},[28,1201,1202],{"class":125}," header ",[28,1204,1185],{"class":34},[28,1206,1188],{"class":34},[28,1208,1209],{"class":1191},"font-weight: 500",[28,1211,1195],{"class":34},[28,1213,1214,1216,1219,1221,1223,1226],{"class":30,"line":52},[28,1215,1179],{"class":1178},[28,1217,1218],{"class":125}," badge ",[28,1220,1185],{"class":34},[28,1222,1188],{"class":34},[28,1224,1225],{"class":1191},"opacity: 0.7",[28,1227,1195],{"class":34},[28,1229,1230],{"class":30,"line":58},[28,1231,1233],{"emptyLinePlaceholder":1232},true,"\n",[28,1235,1236,1240],{"class":30,"line":73},[28,1237,1239],{"class":1238},"s7zQu","return",[28,1241,1242],{"class":125}," (\n",[28,1244,1245,1248,1251,1253,1256],{"class":30,"line":78},[28,1246,1247],{"class":125},"  #div style",[28,1249,1250],{"class":34},"={",[28,1252,111],{"class":125},[28,1254,1255],{"class":34},"}",[28,1257,1258],{"class":125},"#\n",[28,1260,1261,1264,1266,1268,1270],{"class":30,"line":241},[28,1262,1263],{"class":125},"    #div style",[28,1265,1250],{"class":34},[28,1267,408],{"class":125},[28,1269,1255],{"class":34},[28,1271,1258],{"class":125},[28,1273,1274,1277,1279,1281,1283,1286,1289],{"class":30,"line":250},[28,1275,1276],{"class":125},"      #div style",[28,1278,1250],{"class":34},[28,1280,140],{"class":125},[28,1282,1255],{"class":34},[28,1284,1285],{"class":125},"##",[28,1287,1288],{"class":34},"\u002F",[28,1290,1291],{"class":125},"div#\n",[28,1293,1294,1297,1299],{"class":30,"line":261},[28,1295,1296],{"class":125},"    #",[28,1298,1288],{"class":34},[28,1300,1291],{"class":125},[28,1302,1303,1306,1308],{"class":30,"line":438},[28,1304,1305],{"class":125},"  #",[28,1307,1288],{"class":34},[28,1309,1291],{"class":125},[28,1311,1312],{"class":30,"line":447},[28,1313,1314],{"class":125},")\n",[14,1316,1317,1318,1321,1322,1325],{},"和直接写 CSS 的区别在哪里呢？难道只是 ",[25,1319,1320],{},"class"," 变成了 ",[25,1323,1324],{},"style"," ？",[14,1327,1328],{},"——其实最大的区别就是，CSS-in-JS 是“不 Cascading 的”，我声明的标识符 card 就是唯一的，只在我使用它的地方生效，而不会应用在其他的元素上。对于 Web 应用来说，这种有限的预期才是我们通常想要的。这也是我讨论“非结构化”的初衷。",[357,1330],{},[14,1332,1333],{},"所以，就像很多批评的声音一样，TailwindCSS 是不是就是内联样式？没错。但是这就是我们想要的。内联样式（非结构化样式）就是比语义化样式（结构化样式）更适合现代前端工程，正因如此 TailwindCSS 才会越来越流行。所以，如果你还不能接受内联样式和 TailwindCSS，先从淘汰掉你的“结构化样式”开始吧。",[1324,1335,1336],{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sqsOY, html code.shiki .sqsOY{--shiki-light:#8796B0;--shiki-default:#B2CCD6;--shiki-dark:#B2CCD6}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}",{"title":23,"searchDepth":45,"depth":45,"links":1338},[1339,1340,1341,1342,1343],{"id":90,"depth":45,"text":91},{"id":494,"depth":45,"text":495},{"id":862,"depth":45,"text":863},{"id":1004,"depth":45,"text":1005},{"id":1134,"depth":45,"text":1135},"2025-11-25","md",{},"\u002Fposts\u002Fwhy-not-use-structured-styles",{"title":5,"description":16},"posts\u002F2025\u002Fwhy-not-use-structured-styles","Jk_O4y4YArMxcPEsI-mrtR86rUT-SjJXYrOnlwunOs4",[1352,1356],{"title":1353,"path":1354,"stem":1355,"children":-1},"如何实现监听 CSS 变量？","\u002Fposts\u002Fobserve-css-vars","posts\u002F2024\u002Fobserve-css-vars",null,1792500630763]