3万条!important,只有8.7%在盖别人写的样式

3万条!important,只有8.7%在盖别人写的样式
张文保 23 分钟阅读 4,865 阅读
本文目录
  1. 一个站的样式表里,!important到底有多少条?
  2. 那11个“一条都没有”的站,其实一个都不干净
  3. 这些!important是在盖谁的东西?
  4. 那8.7%里,最典型的形态长什么样
  5. 为什么“盖第三方”只占8.7%,和直觉差这么远?
  6. 被盖得最多的,是哪几个属性?
  7. 写在媒体查询里的那四成,该不该算进来?
  8. 盖了又盖:同一个属性上摞两条!important的有多少?
  9. 层叠层这个正解,为什么107个站里只有8个在用?
  10. 这些字节到底花了多少钱?
  11. 这一轮尺子错在哪,两个数为什么方向相反?
  12. 自己站上怎么盘一遍,哪些该删哪些别碰?
  13. 先数一遍,别凭印象
  14. 再按选择器归类
  15. 第三方那一堆:别删,改成显式的一层
  16. 框架工具类那一堆:查配置,不查代码
  17. 自己写的那一堆:从重复声明查起
  18. 最后才考虑层叠层
  19. 常见问题解答
  20. !important到底能不能用?
  21. 我的站上有几百条!important,算多吗?
  22. 删掉!important能提升页面速度吗?
  23. 用Tailwind的站!important更多,是不是说明Tailwind不好?
  24. !important会影响SEO排名吗?
  25. @layer能不能一劳永逸地解决这个问题?
  26. 为什么统计出来只有8.7%是在盖第三方,跟我的感受差这么多?
  27. 这批数据能不能代表我的站?
  28. 权威参考资料
摘要:把107个海外品牌站的样式表逐份拆开数,一共30980条!important,每站中位92条,最多的一个站2745条。按每千条声明折算,中位数是20.9条,四分之一的站超过51.6条。但真正反直觉的是它们盖的对象:命中第三方或平台类名的只有2709条,占8.7%;剩下91.3%落在这个站自己的类名上。换句话说,这堵墙九成是自己砌给自己的。

做前端的人对!important都有一种复杂感情。写的时候知道不该写,写完能下班。等到三个月后要改一个按钮颜色,翻出来发现那行字下面还压着两层!important,才想起来是自己当初留的。

关于它有个流传很广的说法:!important是被平台和第三方插件逼出来的。你装了一个评价插件,它的样式跟你的主题打架,你改不了它的源文件,只好在外面加一条!important盖住。这个故事很顺,也符合每个人的记忆。另一轮实测里我量过图片格式这个决定归谁,那件事上平台确实没给站主留入口。所以这一轮我想知道:样式这一头,是不是也一样。

于是把107个站的样式表逐份拆开,把每一条!important连同它所在的选择器一起数了一遍。数完发现,那个流传很广的说法基本站不住。

一个站的样式表里,!important到底有多少条?

先看体量。107份样式表合计30980条!important,分布极不均匀:

口径数值
每站中位数92条
每站平均289.5条
最多的一个站2745条(charleskeith.com)
一条都没有的站11个
每千条声明的密度中位数20.9条
密度的四分位区间9.5条到51.6条

平均数是中位数的三倍多,说明这是一个典型的长尾:多数站几十上百条,少数几个站把总量拉起来。密度最高的那个站每千条声明有235.8条!important——差不多每四条声明就有一条在喊“听我的”。

这里得先解释一下为什么用密度而不是绝对数。样式表大小差着一个数量级,charleskeith那份707KB、有些站只有几十KB,直接比条数等于在比谁的样式表大。上一轮量样式表覆盖率时也是同一个问题:主样式表中位1149条class定义,这个基数不拉齐,后面所有比较都不成立。所以下文凡是横向比较,一律用“每千条声明多少条!important”。

那11个“一条都没有”的站,其实一个都不干净

先把这个看着很励志的结论掐掉。11个零条站看着像是模范生,把它们的样式表大小拉出来一看就露馅了:这11个站的样式表中位数只有6KB、78条声明,而其余96个站的中位数是212KB、5388条声明。wusthof.com那一份只有7条声明。

也就是说,它们不是没写!important,是我抓到的那份根本不是主样式表——首页上第一个出现的同域样式表,可能只是一个几KB的关键样式片段。这跟在线CSS编辑器吐出来的那份差异清单是同一类误会:拿到一份文件,不等于拿到了完整的那一份。

所以凡是涉及密度的统计,一律只算声明数超过200条的样式表,样本是92份。这条筛选不是为了让数字好看,是为了不让一堆碎片把中位数拽下去。

这些!important是在盖谁的东西?

这是整轮的核心问题,做法也简单:把每一条!important所在的那个选择器抓出来,看它命中的类名属于谁。我准备了九类第三方与平台的类名前缀——Shopify自己注入的、评价插件、弹窗邮件、同意管理、客服、轮播UI库、支付与先买后付、搜索推荐,以及一堆杂七杂八的SaaS。

结果是这样的:

盖的是谁的类名条数出现在几个站
评价与UGC插件106017
轮播与UI库53851
同意管理弹窗36227
Shopify平台注入23820
其他SaaS插件18720
弹窗与邮件订阅16116
在线客服649
支付与先买后付566
站内搜索与推荐436
九类合计2709条,占全部的8.7%

八点七个百分点。剩下的28271条,落在这个站自己的类名上。这个比例跟首页上挂着八个外部域那件事形成了一个有意思的对照:第三方在网络层的存在感很强,在样式层的存在感却小得多。

拿最极端那个站看一眼就更清楚了。charleskeith.com那份707KB的样式表里有13910条声明、2745条!important,其中命中第三方类名的只有37条。它排前面的几个覆盖选择器是.language_switch-list.notification-wrapper.account_menu_modal .notification-wrapper.show——语言切换列表、通知条、账户菜单,全是自家业务组件。这个站不是被插件逼的,是自己跟自己打了两千七百次架。

这个数字第一眼看着不可信,因为它跟每个前端的亲身记忆都对不上。可这两件事并不矛盾:被第三方逼着写的那几条,是你记得最清楚的;数量最多的那一批,恰恰是你写完就忘了的。印象里的比例来自“痛苦程度”,实际的比例来自“条数”,这两把尺子从来就不是一回事。

那8.7%里,最典型的形态长什么样

虽然占比不高,但这一小撮的样子很有代表性。taylorstitch.com有一条选择器是这样的:.yotpo-widget-referral-widget.yotpo-widget-override-css .yotpo-email-container .yotpo-input。注意中间那个类名——yotpo-widget-override-css,插件方自己造了一个名字里带“override”的类,专门留给你去盖。这是供应商对“你会来改我”这件事的正式承认。

ikea.com那条更夸张:#onetrust-consent-sdk #onetrust-banner-sdk.otFloatingRoundedCorner.otRelFont.ot-bottom-left #onetrust-button-g,三个ID加三个类名串在一起,末尾还挂着!importantMDN关于特异性的说明里讲得很清楚,ID的权重比类名高一整个数量级,三个ID堆在一起基本已经是天花板——写到这个份上还要补一条!important,只能说明写的人也不确定自己够不够高,索性一次加满。

另一类更好认:.shopify-payment-button__button--unbranded。这是Shopify注入的加速结账按钮,站主动不了它的源文件,想换个颜色只能从外面盖。hellotushy.com在这一个选择器上盖了16次。

为什么“盖第三方”只占8.7%,和直觉差这么远?

要回答这个,得先承认我这把尺子的一个局限:它只认得出“别人的类名”,认不出“别人写的、但顶着你的类名的代码”。

一个Shopify站买了主题,主题里那几万行CSS不是站主写的,但类名全是.card__.price__.product-form__这种,在我的分类器里全部算作“自己的”。同样,用Tailwind构建的站,那些工具类是构建工具生成的,也算“自己的”。所以8.7%这个数应该读成:盖那些一眼能看出是外来户的东西,只占8.7%;剩下的都盖在“名义上归自己、实际上未必自己写”的代码上。

顺着这个思路往下挖,就挖到了这一轮最有解释力的那张表。我按样式表里的框架痕迹给107个站分组——出现Tailwind的自定义属性和转义类名算Tailwind,出现栅格与组件类算Bootstrap,出现主题模板特征类算Shopify主题,都没有的算看不出框架:

样式表是谁生成的站数每千条声明的!important中位数
Bootstrap1060.7条
Tailwind3043.0条
Shopify主题2417.2条
看不出框架479.4条

用了框架的站,密度是没用框架的站的两倍到六倍半。这个差距比按电商平台分组时大得多。按电商平台分是这样的:

建站平台站数每千条声明的!important中位数样式表中位体积
Salesforce828.1条743KB
Next.js1222.2条138KB
Shopify6519.9条146KB
识别不出平台209.2条86KB

最大与最小之间只差三倍,而且Shopify这个装插件最凶的平台居然排在中间。换句话说,预测一个站有多少条!important,问它用什么CSS框架,比问它用什么电商平台准得多。

为什么框架反而更多?因为这两套框架的工具类本身就带!importantBootstrap的工具类API文档里,important是生成器的一个正式选项;Tailwind的工具类文档里有个感叹号前缀,加上去这条工具类就带!important出来。我这批数据里能直接看到证据:被!important修饰的属性排行里,--tw-bg-opacity出现444次、--tw-text-opacity出现336次——这两个名字只可能来自Tailwind生成的代码。

所以那个流传很广的说法要改一改。把你逼到写!important的,往往不是平台,是你自己选的那套工具——而且它是按设计这么干的。这跟平台锁死一个功能是两码事:Bootstrap和Tailwind从来没有拦着你不加感叹号,开关一直在你手上。

被盖得最多的,是哪几个属性?

把每条!important修饰的属性拆出来数,前几名长这样:

属性次数出现在几个站
display180882
color144655
width138656
font-size115244
background-color105235
height98251

display排第一,而且铺得最开——107个站里82个都有。这一条的含义值得单独说:绝大多数display!important是在做“藏起来”这件事,也就是display:none!important。有人在页面上藏了一块东西,而且藏得非常坚决。

这跟SEO有一点关系,但不是常被吓唬的那一种。用CSS藏内容本身不是作弊,问题在于藏得太彻底会让人忘了它还在。样式表里八成的规则这一页根本用不上,加上HTML空壳那一轮量到的形态,能拼出一个常见场景:结构还在DOM里、样式把它彻底摁住、没人再想起它——直到某天有人改了一行选择器,它突然冒出来。

紧随其后的colorfont-sizebackground-color是另一类:清一色的视觉细节。这些不是跟第三方打架打出来的,是设计稿改了三版、每一版在上一版外面又加了一层。

再往下还有一批更能说明问题的:margin-topmargin-bottompadding-leftpadding-right这四个方向的间距属性各出现七八百次,但每一个都只集中在十几二十个站上。这种“总量大、铺面窄”的形态,说的是少数几个站把整套间距体系用!important重写了一遍——通常发生在一个团队接手别人做的主题、又不敢改主题源文件的时候。选主题时该看代码而不是看演示图,多半就是为了避开这个局面。

写在媒体查询里的那四成,该不该算进来?

如果就这么把30980条端出去,会有一个明显的反驳等着:响应式覆盖是正当用法,那部分不该跟“乱写”混为一谈。

这个反驳有道理,所以我单独数了一遍。写在@media块里的!important有12808条,占全部的41.3%;不过按站看,每站的这个比例中位数只有23.1%——又是长尾,少数几个站的响应式覆盖特别重。

把这四成剔掉,剩下的一万八千条仍然是在无条件地覆盖。另外要说明一句:写在媒体查询里也不等于就正当。真正干净的响应式写法是让不同断点的规则各管各的,靠选择器结构分开;需要靠!important才压得住,通常说明断点之间的规则本来就在互相打架。媒体查询是这条声明出现的位置,不是它的免罪符。

盖了又盖:同一个属性上摞两条!important的有多少?

再看一个更能说明问题的指标:同一个选择器、同一个属性,被!important声明了两次以上。这时候两条声明都带着最高优先级,胜负只能由谁写在后面来决定——MDN对这个关键字的说明里把这个规则写得很直白。

这样的重复声明一共1714条,出现在64个站上,单站最多的govee.com有328条——一个站上有328个属性被摞了两遍。这意味着有人为了盖住一条!important,又写了一条!important。到这一步,优先级机制已经完全失效了,剩下的只有文件顺序在起作用。

顺带看一眼这些声明在文件里的位置。我算了每条!important在样式表字节流里的相对位置,每站的中位数落在0.444——不偏不倚在文件中间,落在最后10%字节里的只占7.9%。这跟“覆盖层都是后来追加在文件末尾”的印象不符。原因也不难想:这些样式表大多是构建工具打包出来的,源文件的先后顺序在打包后已经被打乱,追加的那一层未必排在最后。

层叠层这个正解,为什么107个站里只有8个在用?

这几年CSS其实给出了一个正经答案,叫层叠层。它跟那批停在四年前的前端库面对的是同一个处境:新办法早就有了,只是没人回头去换。MDN对@layer的说明里讲的思路是:把第三方样式、框架样式、你自己的样式各放进一个命名的层,层与层之间的优先级由你显式排定,而不是靠选择器谁写得更长来拼。这正好对着上面那些症状。

107个站里,用了@layer的只有8个。而且用了它也不等于就干净——把这8个站放回按密度排序的队列里,它们从第12名一直散到第71名,高的那个每千声明78.9条,低的只有8.8条。层是个组织工具,不是个清理工具。剩下99个还在用旧办法解决这个问题,而旧办法的痕迹我也量了:写html body这种前缀硬提权的有3个站;ID选择器每站中位6条、最多的一个站168条;四个以上class串在一起的选择器,每站中位79条,最多的一个站2931条。

那个2931条的站,等于把三千条规则的优先级都赌在选择器长度上。这类超长选择器还有个副作用:它们把样式和DOM结构死死绑在一起,结构一动样式就失配,这也是用不上的规则越积越多的成因之一。这不是技术问题,是一个团队在没有约定的情况下,各写各的、互相加码的自然结果。MDN对层叠机制的解释里那句话说得挺好:层叠是一套用来解决冲突的规则,而不是一套用来制造冲突的规则。

这些字节到底花了多少钱?

说到这儿该泼一盆冷水了,免得你以为这是笔大账。

!important这十个字符,占样式表体积的比例,中位数是0.408%,最大的一个站5.820%。107个站合计约303KB,摊到每个站不到3KB。这笔字节账小得几乎不值一提。

所以别指望删掉它们能提速。跟那批给老浏览器发的兼容代码一样,真正的代价不在带宽上。它在别的地方:

第一是改一次的成本。样式表里每多一层!important,下一个人要改这块颜色就得多找一层。这个成本不出现在任何监控面板上,只出现在工时里。

第二是改版的风险。页面上那些没人清的老写法之所以能活很久,很大一部分原因就是没人敢动——覆盖层越厚,敢动的人越少。改版掉流量这件事里最难排查的一类,就是新旧样式互相覆盖导致某块内容视觉上消失、结构却还在。display:none!important正是这类事故最常见的凶器。

第三是它会传染。第三方脚本会自己漂移,而你写下的覆盖层不会跟着漂——插件改了个类名,你那条!important就变成了一条永远不生效的死规则,还留在文件里占位置。一个人在项目里加了第一条!important,下一个人为了盖住它只能也加一条。前面那1714条重复声明,就是这条传染链留下的化石。

这一轮尺子错在哪,两个数为什么方向相反?

这一轮踩的坑不在数据处理上,在样本口径上,而且它一度给了我两个方向完全相反的答案。

这107份样式表来自两个来源:49份是三周前那轮普查里存下的,取的规则是“该站class定义最多的那一份”;58份是这次现抓的,取的规则是“首页上第一份同域样式表”。这两条规则挑出来的不是同一个文件。

直接把两堆放在一起比,得到的是:老那批每千声明30.7条,新这批16.2条——看上去“主样式表更密”。但这两堆是不同的站,比的其实是两批站的差别,不是两种取法的差别。

于是我找了30个两份都有的站,同一个站的两份样式表各算一遍。结果主样式表27.0条、首页第一份38.6条,方向反过来了。这30个站里有6个两份其实是同一个文件,另有22个的主样式表更大。

结论很朴素,也有点扫兴:先前那个“老样本更密”的差异,是两批站不一样造成的,不是取法造成的。这条自查规矩这几轮反复救场——两个数不一样的时候,先确认它们量的是不是同一批东西,再去解释为什么不一样。Vary那一轮前端库版本那一轮都是靠这一步把错结论掐在发布之前。

还有两条口径必须写明。每个站只取了一份样式表,一个站上完全可能有五六份,这一份代表主干但不代表全部。以及那49份是三周前的存档,中间这三周里它们可能发过版;对!important密度这种量级的指标,三周的漂移影响有限,但严格说它不是同一天的快照。

自己站上怎么盘一遍,哪些该删哪些别碰?

这套盘点比图片格式那一套还简单,一个下午能做完。

先数一遍,别凭印象

先确认自己手上那份是不是主样式表。前面11个零条站的教训摆在那儿:首页上第一个出现的样式表,可能只是个几KB的关键片段。看一眼声明条数,几十条的多半是碎片,几千条的才是主干。确认之后再数三个数:!important总条数、声明总条数、两者之比。有了每千声明的密度,才知道自己站在20.9这个中位数的哪一侧。低于10条基本不用管,高于50条说明这套样式已经开始靠蛮力维持。

再按选择器归类

把每条!important所在的选择器抓出来,按类名前缀分成三堆:一堆是第三方与平台的,一堆是框架工具类,一堆是自己写的业务类。这三堆的处置方式完全不同,混在一起看只会得出“太多了得删”这种没法执行的结论。

第三方那一堆:别删,改成显式的一层

这一堆确实没有源文件修改权,删了就打回原形。正确的做法不是删而是收拢——把它们集中到一个命名的层里,或者至少集中到一份文件里,让下一个人一眼看到“这些是用来盖外部插件的”。覆盖别人的东西本身不丢人,丢人的是把它们散落在两千行里。

框架工具类那一堆:查配置,不查代码

这一堆最容易被误判成“团队水平差”。如果你的密度高得离谱、而且属性里出现--tw-开头的自定义属性,那多半不是谁乱写,是构建配置里开了全局的important,或者团队养成了随手加感叹号的习惯。这一堆要改的是约定和配置,逐条删代码没有意义。

自己写的那一堆:从重复声明查起

先找同一个选择器同一个属性上的重复声明——就是前面那1714条那一类。它们百分之百是历史遗留,因为一个属性在同一处不可能真的需要两次最高优先级。删掉靠后那条之前先确认视觉没变,这活儿一次删几条,别整批来。

最后才考虑层叠层

层叠层是好东西,但它不是一个能顺手加上去的补丁。跟content-visibility那类可以直接加的属性不一样,它改的是整套优先级秩序。已有的几千条!important不会因为你套了一层@layer就自动降级——带!important的声明在层叠里的排序规则是反过来的,层的优先级越低反而越难被盖。所以顺序是:先把存量清到能看懂,再引入层,而不是指望层去收拾存量。

常见问题解答

!important到底能不能用?

能,而且有些场景就该用。覆盖你没有修改权的第三方样式、写工具类、做无障碍相关的强制覆盖,这三类都属于正当用途。判据不是用没用,是用完之后下一个人能不能一眼看懂它为什么在那儿。

我的站上有几百条!important,算多吗?

看密度不看条数。每千条声明的中位数是20.9条,低于10条属于干净,20到50条属于常态,超过50条说明这套样式已经在靠优先级硬拼。别拿绝对数跟别人比,样式表大小差着一个数量级。

删掉!important能提升页面速度吗?

基本不能。这十个字符占样式表体积的中位数只有0.408%,一份300KB的样式表里也就1KB多一点。删它的理由是可维护性,不是性能。

用Tailwind的站!important更多,是不是说明Tailwind不好?

不是。Tailwind的工具类带!important是设计使然,加不加由你在类名前面写不写那个感叹号决定。密度高只说明这个项目里用得多,可能是团队约定问题,也可能是构建配置开了全局选项。这是配置层的事,不是框架好坏的事。

!important会影响SEO排名吗?

不会直接影响。它是一个样式优先级关键字,搜索引擎不会因为它给你加分或减分。跟样式表阻塞渲染那条链路也搭不上边,因为它不改变文件何时下载完。间接的关联只有一条:用display:none!important藏起来的内容,在渲染后的页面上是不可见的,而搜索引擎评估的是渲染结果——所以要留心自己藏掉的是不是重要内容。

@layer能不能一劳永逸地解决这个问题?

能解决新写的部分,解决不了存量。带!important的声明在层叠层里的优先级顺序是反的,套一层@layer不会让已有的几千条自动失效。实际路径是先清存量再引入层。

为什么统计出来只有8.7%是在盖第三方,跟我的感受差这么多?

因为记忆是按痛苦程度排序的,统计是按条数排序的。被第三方逼着写的那几条你会记一辈子,自己顺手加的几百条写完就忘。另外这把尺子只认得出“别人的类名”,主题和框架生成的代码顶着看起来像自己的类名,会被算进那91.3%里。

这批数据能不能代表我的站?

样本是107个海外品牌电商站,Shopify占大头,密度分布带着平台色彩。能迁移的是方法:数密度、按选择器归类、找重复声明这三步,在任何一份样式表上都能跑,半天就能得出自己那份答案。

权威参考资料

分享到
标签
版权声明

本文标题:《3万条!important,只有8.7%在盖别人写的样式》

本文链接:https://zhangwenbao.com/css-important-override-attribution-audit.html

版权声明:本文原创,转载与引用请注明作者与原文链接。许可协议: CC BY 4.0

继续阅读
发表评论
分享到微信 或在下方手动填写
支持 Ctrl + Enter 提交