以下是 Eduardo Bouças 的客座文章。 Eduardo 回来跟进他以编程方式处理媒体查询的旅程。 他会告诉你它是怎么开始的,它去了哪里,以及它将如何进行。
一年前,我 踏上了个人探索之旅,寻找使用 CSS 预处理器功能管理断点并在项目中编写媒体查询的最佳方法。 结果是 include-media,一个承诺在 Sass 中实现“简单、优雅和易于维护”媒体查询的库。 但是,这对我来说效果如何呢?
上面的前两个形容词实际上相当主观,因为简单和优雅最终归结为个人喜好——没有必要谈论这些。 相反,我想与您分享我将该库在商业项目中使用一年的个人经验,以及它如何在第三个方面特别帮助了我:可维护性。
回到基础
对类似 include-media 的东西的需求源于开发人员绝对讨厌的一个问题:重复代码。 直到 元素查询 成为现实,我们开始在组件级别思考响应式设计,我们仍然受限于响应全局视窗变化的媒体查询,而不是在元素的上下文中。
因此,开发人员通常会定义“全局断点”,它们实际上只是页面中元素可能改变形式的任意视窗大小。 尽管这是一个简单的概念,可以在原生 CSS 中轻松实现,但预处理器可以立即带来一个优势:能够将这些断点动态定义为变量,允许开发人员仅声明一次并在任何地方引用它们。
$breakpoints: (
'small': 400px,
'medium': 900px,
'large': 1200px
);
在过去的一年中,我一直专注于将该原则推向更远,通过在不同的上下文中使这些断点声明可用。
进入 JavaScript
在 JavaScript 例程中引用窗口宽度并不罕见。 例如,您可能正在执行一段代码,该代码会更改页面中某个元素的行为,但该元素可能只在大视窗中显示。 在这种情况下,如果窗口小于某个值,则完全跳过执行可能是有意义的。
.foo {
display: none;
// We're referencing the name of
// the breakpoint, not the actual value
@include media('>=large') {
display: block;
}
}
function doStuff() {
// Bollocks, we're still referencing the value here!
if (window.innerWidth < 1200) {
return;
}
doHeavyStuff();
}
但我们又回到这里——那个“某个值”是我们自豪地在样式表中仅声明一次的断点之一,但我们现在重新声明了它的值,因为名称 large
在 JavaScript 中没有任何意义。 关于保持事物 DRY 的说法太少了。
为了解决这个问题,有一个名为 include-media-export 的插件,它是 SCSS 例程(将有关断点的信息写入 DOM)和一个用于读取和处理它的小型 JavaScript 实用程序的组合。 使用该插件,我可以重写上面的函数而不重新声明断点,因此如果有人决定将来更改它的值,一切都会好起来。
function doStuff() {
// Yay, we're DRY again!
if (im.lessThan('large')) {
return;
}
doHeavyStuff();
}
im
对象通过 im.getActive
公开当前活动断点的名称,而 im.greaterThan
和 im.lessThan
确定窗口是否比某个断点更宽或更窄。
这不是一个新概念,许多其他人过去也写过这方面的内容(主要是 Les James 和 Mike Herchel)。 这里的想法是将所有内容绑定在同一个生态系统中,为开发人员提供无缝的体验。
还有布局
我本身没有使用任何网格系统,但我发现拥有仅用于布局目的的某些类很方便。 当应用于元素时,这些类将定义其宽度,而不是在单个选择器上手动设置它——这会使样式表保持井井有条,并使标记更具语义性。
我过去会以类似这样的方式结束
.col--1-2 {
width: 50%;
}
.col--1-3 {
width: 33.3333%;
}
.col--2-3- { /* etc. */ }
在阅读了 Harry Robert 关于 BEMIT 的文章 之后,我对他的“响应式后缀”方法非常感兴趣。 他建议包含一个包含断点名称的类,以描述元素在该屏幕大小下的状态(例如 .class-name@breakpoint
)。 这个想法导致了 include-media-columns,一个根据 include-media 中定义的断点生成列类的插件,遵循 BEMIT 的命名约定。
让我们以之前定义的断点列表为例,并想象一个用户资料组件,它必须
- 在小视窗中使用其容器的全部宽度
- 在中等视窗中占据一半的宽度
- 在大视窗中占据三分之一的宽度

我可以通过在 HTML 中为元素赋予正确的类来简单地定义该行为,而无需编写任何媒体查询甚至触碰 CSS。 它带来了语义性强且有意义的标记的巨大优势。
<div class="user-profile col col--1-1 col--1-2@medium col--1-3@large">
<!-- User profile -->
</div>
后缀在 col--1-1
上有意被省略,作为移动优先方法的一部分,使其成为组件的“默认状态”。 如果您关心支持旧浏览器并且不希望它们获得移动视图,那么 这篇文章 中描述的 $im-media-support
标志可以解决您的问题。
使它成为工作流程的一部分
这一切都很好,但将此部分集成到您的工作流程中有多容易呢? 如果安装和更新很痛苦,那么试图提高可维护性就有点背离了初衷。 include-media 和插件都是 semver 版本的,并且可以作为 Bower 和 NPM 包使用,因此我在开始项目时将它们简单地包含为常规依赖项。
$ npm install include-media include-media-export include-media-columns --save
然后,我将 SCSS 文件导入到我的样式表中。
@import 'path/to/node_modules/include-media/dist/include-media';
@import 'path/to/node_modules/include-media-export/dist/include-media-export';
@import 'path/to/node_modules/include-media-columns/include-media-columns';
Export 插件不需要任何额外的配置,而 Columns 要求您指定要为页面生成多少个细分。
// I want to be able to divide the page in halves, thirds and fifths
@include im-columns(2, 3, 5);
总结
现在所有断点都定义在一个集中位置,任何更改或添加都会传播并提供给脚本和布局——我认为这缓解了我对 DRY 的困扰。
我并不想向您推销这种工作流程,我当然也不声称对任何突破性想法拥有功劳。 所有这些东西以前都存在,但我希望为开发人员提供工具,将所有内容捆绑在一起,使开发更轻松。
我迫不及待地想看到元素查询、CSS 变量、CSS Grid Layout 和其他现代 API 使本文中描述的所有内容完全过时——但在那之前,我对 include-media 和其贡献者一年(和 460 个 GitHub 星星!)给我带来的开发工作流程感到满意。
感谢您分享您使用这些工具的经验。
我处理三分之一宽度的做法是这样的
这样,我就可以按照长期想要的方式设置它,并且可以添加边距/填充,而不会出现奇怪的像素/亚像素问题(1 像素浏览器碰撞),并且第一个宽度为我提供了向后兼容性。
好奇的是,为什么要将 JavaScript 与断点一起使用? 是某种在线应用程序要求吗? 我无法想象在“标准”网站上需要这样做...
我看到了很多情况,JavaScript 会介入,根据断点更改内容。
假设您想要一个手风琴小部件在较宽的视窗中变为选项卡界面,或者您需要将任何东西转换为其他东西,因为视窗宽度发生了变化。
我使用类似的解决方案,通过 HTML 伪元素将 CSS/Sass 媒体查询公开给 JavaScript。 通常,我还将它与类似 https://www.filamentgroup.com/examples/rwd-nav-patterns/ 的东西结合起来,处理内容或菜单需要折叠成其他内容的情况。
感谢您的这篇文章。对于 JS,我更喜欢使用带有媒体查询的 Javascript,并使用很棒的库 http://wicky.nillia.ms/enquire.js/
它允许您在进入断点时定义一些行为/侦听器,并在离开断点时移除它们。
我有一个获取断点的小函数。我在 SCSS 中将它们放在一个映射中,然后将它们写入
上的
伪元素。然后我在 JS 中读取它。我将我的存储为 EM,所以我进行一个计算来根据字体大小获得实际的 px 断点。
http://codepen.io/alexmccabe/pen/ojRegx
我可能错了,但在这个计算上,它在大多数情况下都能正常工作。您确实需要注意的是,JS 会考虑滚动条,而 CSS 不会,因此断点会稍微偏离。
拜托,"col col–1-1 col–1-2@medium" 一点也不语义化!
你怎么能写出这样的代码?
CSS 类需要语义化吗?
@Amelia: 嗯,是的。CSS 的全部意义在于将内容与表现分离——我们可以只使用标签和内联样式。
您不需要在 CSS 类中使用语义化。类是一个开发者工具,所以您根据自己的需要使用它们。例如,在我们的例子中,我们使用 BEM 规范,因为它可以极大地减少命名冲突的可能性。CSS 框架(如 Bootstrap)则倾向于为它们的类使用非常以开发者为中心的简短名称。我不像喜欢 BEM 那样喜欢它,但它同样有效。
HTML 是您需要语义化的地方,因为它在其中有价值。在类中,语义化本身没有真正的价值:没有任何东西会分析您的类的好坏,或者因为您使用了正确的类名而为您提供更好的搜索排名或更好的可访问性。
您可以决定您不需要在 CSS 类中使用语义化。
在这种情况下,您不能假装 "col-1 col-1-1 col-1-2@large" 是非常语义化的。因为它不是。
也许 "描述性" 比 "语义化" 更适合使用。
Vesa 说得对。CSS 类只对开发者和浏览器有意义。HTML 应该语义化,因为它描述了内容,但类实际上可以是任何东西。"col col–1-1 col–1-2@medium" 好看吗?不,但它也不必好看。