解决Sass媒体查询的重复问题
在Sass中有很多方法可以解决媒体查询这个问题,但是其中一些方法存在相同的问题。一开始我会还原这个问题,然后提供我的解决方案,最后列出这个方案对工作流程的好处。
问题
假如我们把网站的Sass样式分离成多个像header.scss
,hero.scss
,cards.scss
等等这样的组件。为他们添加各自的响应式样式最简洁的做法是,在各自的组件文件中添加相应的响应式样式。为了实现这样的效果,最简单的做法就是,定义一系列针对不同尺寸屏幕的mixin
然后使用@content
。
@mixin tablet {
@media (min-width: 768px) and (max-width: 1024px) {
@content;
}
}
@mixin desktop {
@media (min-width: 1024px) {
@content;
}
}
这些mixins
会将外部传进来的样式片段,放置在@content
出现的地方。
就像这样使用:
p {
font-size: 16px;
@include tablet {
font-size: 18px;
}
@include desktop {
font-size: 20px;
}
}
这样很好,很整齐。它告诉我们,一个p元素里对两个不同的设备有不同的响应式设计。但是这样带来的问题是,在编译后CSS文件中,每个mixin
中的媒体查询会重复地出现,就像这样:
p {
font-style: 18px;
}
@media (min-width: 768px) and (max-width: 1023px) {
p {
font-style: 20px;
}
}
@media (minwidth: 1024px) {
p {
font-style: 25px;
}
}
上面是Write Better Media Queries with
Sass一文中提供的例子,只是为了举例说明这个问题。
这个例子仅仅涵盖了一个p
标签,当存在很多需要响应式设计的元素的时候,这里将存在大量的重复。
对于小型网站,这样简单地将媒体查询mixin
放在元素中可能并没有什么不妥,但是如果是大型网站,这样做会使CSS文件很快臃肿起来。如果Sass团队能解决这个问题,那再好不过了,但是我确定,这对编译速度来说是个很大的挑战。
简单的解决方案
在这个解决方案中,你可以将响应式样式放在每个组件中相应位置,并且没有重复的媒体查询,一切看起来都很完美,整齐,且易于维护。
步骤 1. 创建一个 media-queries.scss 文件
为了避免重复的媒体查询,并且让代码看起来更整齐,我们在一个单独的Sass文件中定义媒体查询,然后调用这个文件。比如media-queries.scss
// small screen size (sm)
@media (min-width: 801px) {
...
}
// medium screen size (md)
@media (min-width: 992px) {
...
}
为了看起来简单一点,在这里仅仅为小尺寸和中等尺寸屏幕定义了两个主要的断点。
步骤 2. 为每个元素创建一个响应式mixin
就拿banner.scss
来说,利用移动优先原则,在文件的底部定义一系列响应式mixin
。这些mixin
将会在media-queries.scss
中相应的断点处调用。
确保为mixin
添加一个能反映出相应断点的后缀,像前面的例子中的,small(sm)
,medium(md)
。比如说,如果一段样式处于小尺寸屏幕的断点,这个mixin
名字就可以取为,元素的名称+sm
后缀。
在mixin
中为你准备适配的屏幕尺寸添加所有相应的样式。如:
.banner {
text-align: center;
font-size: 14px;
}
// called in media-queries.scss
@mixin banner--sm() {
.banner {
font-size: 20px;
}
}
@mixin banner--md() {
.banner {
text-align: left;
font-size: 25px;
}
}
步骤 3. 在media-queries.scss
中调用这些mixin
回到media-queries.scss
文件,我们可以在指定的媒体查询中,调用相应的mixin
。
// small screen size (sm)
@media (min-width: 801px) {
@include banner--sm();
}
// medium screen size (md)
@media (min-width: 992px) {
@include banner--md()
}
好了,现在所有的响应式样式片段都定义在相应在的组件文件当中,然后被调用在媒体查询中相应断点处。整个编译后的CSS文件中没有重复的,冗余的媒体查询语句。
media-queries.scss
输出的CSS文件:
@media (min-width: 801px) {
.banner {
font-size: 20px;
}
}
@media (min-width: 992px) {
.banner {
text-align: left;
font-size: 25px;
}
}
最后,在每个断点处会调用很多个mixin
,这样编译后的CSS文件就不会变得臃肿,同时你也很清楚所有的响应式样式片段在什么地方:
@media (min-width: 801px) {
@include home-cta--sm();
@include twitter-testimonials--sm();
@include site-footer--sm();
@include feature-section--sm();
@include feature-item--sm();
@include post-meta--sm();
@include social-share--sm();
@include hero--sm();
@include feature-page--sm();
}
优点
想东想西在压缩文件体积方面,效果并不是很明显,但是对我来说,能减则减。另外一方面,重复的媒体查询并不会太大地影响性能。
优点主要还是体现在优化工作流程方面。
团队易读性 和 标准实践
回到最初嵌套的媒体查询方法,我也喜欢这个方法,但是如果在一个非常庞大的组件文件中的不同层级使用这种嵌套,会使代码非常不整齐。当然,也可以结合BEM,使得嵌套的层数不超过2
层。这种搭配BEM的解决方案其实还不错。
尽管如此,你和你的同事还是要上下滚动来查找哪些元素存在响应式设计。因为你必须弄清那些存在响应式设计的元素,这样,组件文件又不应该过长。使用我的解决方案,你和你的团队会很清楚在任意一个文件中元素媒体查询语句的位置。而且你可以在媒体查询区域寻找元素,如果没有,那么这个元素不会对任何的断点作出响应。
也可以在media-queries.scss
文件中,查找那些存在响应式设计的元素。我们已经在一个组件数量超过30
个的网站运用了这种方法,它的可伸缩性很好。
可以很好地利用移动优先原则
最后,如果使用了移动优先原则,在组件文件中你将会从上到下依次看到小屏幕到大屏幕对应的样式。这样就会减少对不同屏幕尺寸所在位置的额外关注。
一些缺点
这个方案也有一些缺点。比如,当我们删除了一个元素,就必须回到media.scss
文件中删除相应的调用,不然就会抛出一个编译错误,不过也有一点好处是,你不用太过注意它,反正都会抛出错误提醒你。还有就是,可能会忘记在media-queries.scss
添加mixin
。
重构的时候也会比嵌套的媒体查询更麻烦。
在实际开发当中,特别是在回顾的时候,拥有一个结构良好的,有组织的媒体查询方法可以简化工作并且让我们更迅速地debug。
本文根据@Dominique
Briggs的《Solving
Sass's media query duplication problem and enhancing your
workflow》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:https://medium.com/front-end-developers/the-solution-to-media-queries-in-sass-5493ebe16844#.vakrbmst8。