通用优先 CSS:移动优先的新思维
已发表: 2022-03-10我认为可以肯定地说,Ethan Marcotte 的响应式 Web 设计对全世界的 Web 开发人员来说是一个受欢迎的启示。 它引发了全新的设计思维浪潮和精彩的前端新技术。 经常被鄙视的 m dot 网站的统治也结束了。 在同一时代,几乎同样具有影响力的是 Luke Wroblewski 的 Mobile First 方法——建立在 Marcotte 令人印象深刻的基础之上的坚实改进。
这些技术是大多数 Web 开发人员生活的基石,它们为我们提供了很好的服务,但可惜的是,时代在变,开发人员不断迭代。 随着我们提高方法的效率和项目要求变得更加复杂,新的挫折出现了。
通用优先之旅
我无法准确指出是什么让我改变了我编写 CSS 的方式,因为这对我来说真的是一种自然的进步,几乎是下意识地发生的。 回想起来,我认为这更像是我工作的开发环境的副产品。与我一起工作的团队有一个很好的 SCSS 工作流程,其中包含一个漂亮的小混入,可以轻松地在我们的 CSS 声明中添加断点。 您可能使用了类似的技术。
这个奇妙的小 SCSS mixin 突然使编写超细粒度的媒体查询变得容易。 假设一个看起来像这样的传记块:
.bio { display: block; width: 100%; background-color: #ece9e9; padding: 20px; margin: 20px 0; @include media('>=small') { max-width: 400px; background-color: white; margin: 20px auto; } @include media('>=medium') { max-width: 600px; padding: 30px; margin: 30px auto; } @include media('>=large') { max-width: 800px; padding: 40px; margin: 40px auto; } @include media('>=huge') { max-width: 1000px; padding: 50px; margin: 50px auto; } }
图。1。 具有级联媒体查询的典型移动优先
这很好用——我过去写过很多这样的 CSS。 然而,有一天我突然意识到,随着设备宽度的增加覆盖 CSS 声明是没有意义的。 为什么要声明一个 CSS 属性,它只会在下面的声明中被覆盖?
这就是导致我开始编写分区媒体查询的原因,而不是像图 1 中的示例那样向上(或向下)级联的更常见的媒体查询方法。
我没有编写随着屏幕尺寸增加而向上级联的媒体查询,而是开始创建有针对性的媒体查询,将样式封装在所需的屏幕宽度上。 媒体查询 mixin 将在这里真正发挥作用。 现在我的 SCSS 媒体查询开始看起来像这样:
.bio { display: block; width: 100%; padding: 20px; margin: 20px 0; @include media('>=small', ' < medium') { max-width: 400px; margin: 20px auto; } @include media('>=medium', ' < large') { max-width: 600px; padding: 30px; margin: 30px auto; } @include media('>=large', ' < huge') { max-width: 800px; padding: 40px; margin: 40px auto; } @include media('>=huge') { max-width: 1000px; padding: 50px; margin: 50px auto; } }
图 2。 划分媒体查询的示例
这种新方法对我来说更直观,它减少了从前一个断点重置样式的次数,并且使 CSS 更易于阅读。 更重要的是,它使媒体查询以更重要的方式自我记录。
尽管如此,我仍然不是 100% 满意,似乎还有一个重大问题需要克服。
移动优先的问题
移动优先的问题在于,根据定义,您很可能必须在后续媒体查询中覆盖移动优先样式。 这感觉有点像反模式。
所以 - 对我来说 - 答案很明显:让我们将媒体查询划分的想法推向其合乎逻辑的结论 - 我们还将移动特定样式划分为他们自己的媒体查询。 我知道,我知道,这违背了我们多年来学到的共同惯例。 “移动优先”如此普遍,以至于它通常是招聘经理会问的“技能”问题之一。 因此,任何替代方案肯定都是错误的,不是吗? 这通常是人们在一遍又一遍地首先说出移动时向我摇头的部分。
好的,所以我们将打破移动优先的教条,将我们所有的样式划分为相关的媒体查询。 我们现在剩下的是在 CSS 选择器上声明的纯通用样式,所有其他特定于设备的样式都封装在仅适用于相关屏幕尺寸的媒体查询中。 我们现在有Generic First CSS :
.bio { display: block; width: 100%; @include media('>=0', ' < small') { padding: 20px; margin: 20px 0; } @include media('>=small', ' < medium') { max-width: 400px; margin: 20px auto; } @include media('>=medium', ' < large') { max-width: 600px; padding: 30px; margin: 30px auto; } @include media('>=large', ' < huge') { max-width: 800px; padding: 40px; margin: 40px auto; } @include media('>=huge') { max-width: 1000px; padding: 50px; margin: 50px auto; } }
图 3。 通用优先 CSS 示例
是的,有更多的媒体查询,但是,我认为这是一个好处,任何开发人员现在都可以查看这个 CSS 并准确地查看在每个屏幕尺寸上应用了哪些样式,而无需挑选媒体的认知开销 -查询特异性。
这对于不熟悉代码库的人甚至未来的您来说都非常有用!
何时不划分
有时媒体查询划分是一种负担,在某些情况下,旧的 >= 媒体查询很好。 请记住,我们要做的只是避免属性覆盖。
开发工具 Bliss
编写分隔的 Generic First CSS 的一个主要意外后果是您将从开发人员工具样式面板中获得的体验。 如果没有媒体查询级联,您现在将更清楚地了解应用了哪些样式 - 您将不会有一个样式面板,其中充满了从覆盖的媒体查询规则中删除的声明 -噪音消失了! 这——对我来说——是 Generic First CSS 技术的最大好处之一。 它为 CSS 调试体验带来了一点额外的理智,这是物超所值的。 稍后再谢谢我。

性能影响
所以所有这些 Generic First CSS 的好处开始听起来不错,但我认为还有一个我认为需要解决的最后一个关键问题。 这是关于性能优化的主题。 现在我还不确定,但我有一种暗示,完全划分的媒体查询可能会对性能产生轻微的好处。

浏览器执行称为计算样式计算的渲染任务。 这是浏览器计算在任何给定时刻需要将哪些样式应用于元素的方式。 此任务始终在初始页面加载时执行,但也可以在页面内容更改或发生其他浏览器操作时执行。 您可以提高流程速度的任何提升都将非常适合初始页面加载,并且可能对您网站页面的生命周期产生复合影响。
所以回到通用的第一个 CSS:是否存在与浏览器必须解决大量级联媒体查询的 CSS 特异性相关的性能问题?
为了回答这个问题,我设计了一个测试用例,可用于衡量任何速度优势或确实存在的劣势。
测试用例
测试用例由一个基本的 HTML 页面组成,该页面输出“bio”块 5000 次,每个块的标记相同,但类略有不同(数字微分器),该块的 CSS 也输出 5000 次, 类名是唯一不同的地方。 输出的 CSS 通过一个名为 CSS MQPacker 的工具进行管道传输,这有助于通过将特定媒体查询的所有单独实例合并为一个来显着减少使用大量内联媒体查询的 CSS 的文件大小——这是一个很棒的工具,可能会受益最现代的 CSS 代码库——我通过测试项目 package.json 中的 npm 任务将它用作独立的 cli 工具,您也可以将它用作 postcss 插件,非常方便!
第一个测试用例是移动优先级联媒体查询示例,第二个测试用例是 CSS 的通用第一个分区变体。 这些案例的 CSS 有点冗长,可能可以用更简洁的术语来编写,但它实际上只是作为测试论证的粗略示例。
该测试针对桌面 Google Chrome v70 中的每个 CSS 变体运行了 20 次,虽然不是大量数据,但足以让我大致了解性能增益/损失。
我选择使用的测试指标是:
- 总页面加载时间
使用 <head> 开头和 <body> 结尾处的 Performance API 标记检查页面加载时间的基本指标 - 重新计算样式
开发工具性能窗格中的时间。 - 整体页面渲染
开发工具性能窗格中的时间。

结果表(所有时间以毫秒为单位)
移动优先 | 通用优先 | ||||||
---|---|---|---|---|---|---|---|
加载时间 | 计算样式 | 总渲染时间 | 加载时间 | 计算样式 | 总渲染时间 | ||
1135 | 565.7 | 1953年 | 1196 | 536.9 | 2012 | ||
1176 | 563.5 | 1936年 | 1116 | 506.9 | 1929年 | ||
1118 | 563.1 | 1863年 | 1148 | 514.4 | 1853年 | ||
1174 | 568.3 | 1929年 | 1124 | 507.1 | 1868年 | ||
1204 | 577.2 | 1924年 | 1115 | 518.4 | 1854年 | ||
1155 | 554.7 | 1991 | 1177 | 540.8 | 1905年 | ||
1112 | 554.5 | 1912年 | 1111 | 504.3 | 1886年 | ||
1110 | 557.9 | 1854年 | 1104 | 505.3 | 1954年 | ||
1106 | 544.5 | 1895年 | 1148 | 525.4 | 1881 | ||
1162 | 559.8 | 1920 | 1095 | 508.9 | 1941年 | ||
1146 | 545.9 | 1897年 | 1115 | 504.4 | 1968年 | ||
1168 | 566.3 | 1882年 | 1112 | 519.8 | 1861 | ||
1105 | 542.7 | 1978年 | 1121 | 515.7 | 1905年 | ||
1123 | 566.6 | 1970 | 1090 | 510.7 | 1820 | ||
1106 | 514.5 | 1956年 | 1127 | 515.2 | 1986年 | ||
1135 | 575.7 | 1869 | 1130 | 504.2 | 1882年 | ||
1164 | 545.6 | 2450 | 1169 | 525.6 | 1934年 | ||
1144 | 565 | 1894年 | 1092 | 516 | 1822 | ||
1115 | 554.5 | 1955年 | 1091 | 508.9 | 1986年 | ||
1133 | 554.8 | 2572 | 1001 | 504.5 | 1812 | ||
平均 | 1139.55 | 557.04 | 1980 | 1119.1 | 514.67 | 1903.15 |
图 6。 20 次测试运行测量移动优先与通用优先 CSS 的关键负载/渲染指标。
从我公认的小数据集来看,我最初的怀疑似乎是正确的。 平均而言,我看到样式重新计算任务花费的时间减少了 42 毫秒,速度提高了 7.6%,因此整体渲染时间也减少了。 差异并不令人兴奋,但它是一种改进。 我不认为数据集大到足以 100% 确定,并且测试用例有点不切实际,但我很高兴没有看到性能下降。
我很想看到将通用的第一方法应用于以移动优先方式编写的现实世界现有代码库 - 之前的度量标准对于日常实践会更加现实。
如果有人对如何在更广泛的迭代中自动执行此测试有任何建议,请在评论中告诉我! 我想一定有一个工具可以做到这一点。
结论
回顾一下这种新开发方法的好处......
- 完全符合预期的 CSS,无需再猜测;
- 自记录媒体查询;
- 更好的开发工具体验;
- 渲染速度更快的页面。
我想我不是唯一一个支持以这种风格编写 CSS 的人。 如果您已经采用了通用的第一思维方式,那就欢呼吧! 但如果没有,我想你会非常喜欢它带来的好处。 我个人从整洁的开发工具体验中受益匪浅,这本身对许多开发人员来说是一个巨大的积极因素。 这种编写媒体查询的方式的自我记录性质也将对您自己和更广泛的团队(如果您有的话)有益。 最后,这些好处不会让您在性能方面付出任何代价,而且事实上已经证明可以带来边际速度提升!
最后一句话
像所有开发方法一样,它可能并不适合所有人,但我很自然地陷入了 Generic First CSS,我现在认为它是一种有价值的工作方式,它为我提供了移动优先的所有好处,并添加了一些积极的新功能前端开发的艰巨工作,再简单不过了。
资源
测试用例回购
如果您想启动测试用例并自己尝试一下,您可以在 GitHub 上找到它,我很乐意看到其他人的一些报告。
工具
- CSS MQPacker
- 包括媒体