CSS样式覆盖实测:!important只有8.7%在盖第三方
本文目录
- 样式表里,!important的总量与密度是什么水平?
- 那11个零条站为什么其实并不干净
- 这些!important覆盖的类名归属于谁?
- 8.7%里最典型的覆盖形态是什么样
- 覆盖第三方样式只占8.7%,为什么和直觉差这么远?
- CSS样式表里,!important最常修饰哪几个属性?
- 写在媒体查询里的覆盖声明该不该计入?
- 同一属性上摞两条!important的重复声明有多少?
- 107个站里只有8个用了层叠层@layer,为什么?
- !important的字节账很小,真正的代价在哪?
- 样本口径如何让两个密度数字方向相反?
- 自查样式表的覆盖密度该按什么顺序盘?
- 先确认主样式表,再数密度
- 按选择器把覆盖归成三类
- 第三方那一类:不要删,收拢成显式的一层
- 框架工具类那一类:查构建配置,不逐条查代码
- 自己写的那一类:从重复声明查起
- 最后才引入层叠层
- 常见问题解答
- !important到底能不能用?
- 我的站上有几百条!important,算多吗?
- 删掉!important能提升页面速度吗?
- 用Tailwind的站!important更多,是不是说明Tailwind不好?
- !important会影响SEO排名吗?
- @layer能不能一劳永逸地解决这个问题?
- 为什么统计出来只有8.7%是在盖第三方,跟我的感受差这么多?
- 这批数据能不能代表我的站?
- 权威参考资料
摘要:把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插件 | 1060 | 17 |
| 轮播与UI库 | 538 | 51 |
| 同意管理弹窗 | 362 | 27 |
| Shopify平台注入 | 238 | 20 |
| 其他SaaS插件 | 187 | 20 |
| 弹窗与邮件订阅 | 161 | 16 |
| 在线客服 | 64 | 9 |
| 支付与先买后付 | 56 | 6 |
| 站内搜索与推荐 | 43 | 6 |
| 九类合计 | 2709条,占全部的8.7% | |
九类加起来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加三个类名串在一起,末尾还挂着!important。MDN关于特异性的说明里讲得很清楚,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中位数 |
|---|---|---|
| Bootstrap | 10 | 60.7条 |
| Tailwind | 30 | 43.0条 |
| Shopify主题 | 24 | 17.2条 |
| 看不出框架 | 47 | 9.4条 |
用了框架的站,密度是没用框架的站的两倍到六倍半。这个差距比按电商平台分组时大得多。按电商平台分组的结果是这样:
| 建站平台 | 站数 | 每千条声明的!important中位数 | 样式表中位体积 |
|---|---|---|---|
| Salesforce | 8 | 28.1条 | 743KB |
| Next.js | 12 | 22.2条 | 138KB |
| Shopify | 65 | 19.9条 | 146KB |
| 识别不出平台 | 20 | 9.2条 | 86KB |
最大与最小之间只差三倍,而且Shopify这个装插件最凶的平台排在中间。预测一个站有多少条!important,问它用什么CSS框架,比问它用什么电商平台准得多。
为什么框架反而更多?因为这两套框架的工具类本身就带!important。Bootstrap的工具类API文档里,important是生成器的一个正式选项;Tailwind的工具类文档里有个感叹号前缀,加上去这条工具类就带!important出来。这批数据里能直接看到证据:被!important修饰的属性排行里,--tw-bg-opacity出现444次、--tw-text-opacity出现336次,这两个名字只可能来自Tailwind生成的代码。
所以开头那个流传很广的解释要改一改。把你逼到写!important的,往往是你自己选的那套工具,而且它按设计就这么生成。这跟平台锁死一个功能是两码事:Bootstrap和Tailwind从来没有拦着你不加感叹号,开关一直在你手上。
CSS样式表里,!important最常修饰哪几个属性?
把每条!important修饰的属性拆出来数,前几名是这样:
| 属性 | 次数 | 出现在几个站 |
|---|---|---|
| display | 1808 | 82 |
| color | 1446 | 55 |
| width | 1386 | 56 |
| font-size | 1152 | 44 |
| background-color | 1052 | 35 |
| height | 982 | 51 |
display排第一,而且铺得最开,107个站里82个都有。这一条的含义值得单独说:绝大多数display加!important做的都是同一件事,写法是display:none!important。有人在页面上藏了一块东西,而且藏得非常坚决。
这跟SEO有一点关系,但不是常被吓唬的那一种。用CSS藏内容本身不算作弊,问题在于藏得太彻底会让人忘了它还在。样式表里八成的规则这一页根本用不上,加上HTML空壳那一轮量到的形态,能拼出一个常见场景:结构还在DOM里、样式把它彻底摁住、没人再想起它,直到某天有人改了一行选择器,它突然冒出来。
紧随其后的color、font-size、background-color属于另一类,清一色的视觉细节。这些多半不是跟第三方打架打出来的,是设计稿改了三版、每一版在上一版外面又加了一层。
再往下还有一批更能说明问题的:margin-top、margin-bottom、padding-left、padding-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个用了层叠层@layer,为什么?
这几年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的字节账很小,真正的代价在哪?
先把字节账算清楚,免得把它当成一笔大账。
!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占大头,密度分布带着平台色彩。能迁移的是方法:数密度、按选择器归类、找重复声明这三步,在任何一份样式表上都能跑,半天就能得出自己那份答案。
权威参考资料
本文标题:《CSS样式覆盖实测:!important只有8.7%在盖第三方》
本文链接:https://zhangwenbao.com/css-important-override-attribution-audit.html
版权声明:本文原创,转载与引用请注明作者与原文链接。许可协议: CC BY 4.0