Sass函数功能——rem转px

本文由大漠根据David Walsh的《REM to PX Browser Function with Sass》所译,整个译文带有我们自己的理解与思想,如果译得不好或不对之处还请同行朋友指点。如需转载此译文,需注明英文出处:http://davidwalsh.name/rem-px-browser-function-sass,以及作者相关信息

性能对于Web前端开发人员是必备的一项技能。CSS3和HTML5的新特性帮助我们改善了应用程序,但有时这些特性并没有得到很好的支持。这就是优雅降级。你想在新的浏览器中使用这些特性,但不能忽视对传统浏览器的支持。我最近开始在做一个新的项目,他必须要支持IE8。由于级联问题的嵌套,使用EM单位时不好把控,所以我决定在这个项目中开始使用有用的rem单位,这样更容易理解和维护。这种方法的主要问题是IE8不支持rem单位。最后,我们需要针对于这种情景创建一个后备方案:使用px单位做降级处理。

性能和编码速度

你会发现,在网络上关于rem px mixin有很多有用的话题和讨论,本文以这些文章做了一定的参考研究。大部分他们都是依赖于相同的技术,使用Sass mixin产生两次CSS属性,如:

SCSS

.header {
 @include rempx(margin, 1rem 2rem);
}

编译出来的CSS:

CSS

.header {
 margin:16px 32px; /* Fallback */
 margin:1rem 2rem;
}

主要的问题是每使用这个mixin什么重复写CSS样式,这样整个CSS样式体重就会增长。这种技术有两个属性,而不是一个。第一个是用在不支持Rem单位的浏览器上。代码不怎么干净,但它是灵活的和易于维护。因为我们知道rem单位在IE8中不被支持,但是,我们要是有两不同的样式表呢?

第二个问题是,使用上面的方法存在输入速度问题。每次你想添加一个新的CSS属性都得输入一次@include rempx(property,values),这不是最优秀的方法。

提供不同的样式表

我们的第一个目标是有两个分开的样式表:

  • main.css文件用于标准浏览器,使用rem单位
  • main-ie.css文件用于IE8浏览器,使用px单位

这两个文件将会有完全相同的CSS内容,只是单位不同。

Nicolas Gallagher向我们展示了如何在标准浏览器和IE浏览器中加载不同的CSS文件,如下所示:

<!--[if (gt IE 8) | (IEMobile)]><!-->
  <link rel="stylesheet" href="/css/main.css">
<!--<![endif]-->

<!--[if (lt IE 9) & (!IEMobile)]>
  <link rel="stylesheet" href="/css/main-ie.css">
<![endif]-->

标准浏览器将下载main.css文件,而IE浏览器下将下载main-ie.css文件。这将确保客户端没有下载这两个CSS文件。

我们的第二个目标是使用@mixin技术,所以我们只需要维护一个文件。

Sass解决文案

现在我们知道如何调用不同的样式表,我们现在需要生成两个CSS文件。这个时候我们需要把Sass引进来。为了使用这个示例尽量的简单,我不会给Sass使用一个特殊的文件结构,只是一群.scss文件。这是我们示例中使用的结构:

  • 一个空的css文件夹
  • 一个sass文件夹,里面有四个文件:main.scssmain-ie.scss_function.scss_module-example.scss

main.scss

// 配置
$px-only:false;

// 导入Sass的函数文件
@import "functions";

// 导入网站的模块文件
@import "module-example.scss";

main-ie.scss

// 配置
$px-only:true;

// 导入Sass的函数文件
@import "functions";

// 导入网站模块文件
@import "module-example.scss";

_function.scss

$pixelBase : 16; /* 1 */

@function parseInt($n) {
    @return $n / ($n * 0 + 1); /* 2 */
}

@function u($values){ /* 3 */

      $list: (); /* 4 */

      @each $value in $values { /* 5 */

            $unit : unit($value); /* 6 */
            $val  : parseInt($value); /* 2 */

            @if ($px-only) and ($unit == 'rem') { /* 7 */
                  $list: append($list, ($val * $pixelBase) + px); /* 7 */
            }

            @else if($unit == 'px') or ($unit == 'rem'){ /* 8 */
                  $list: append($list, $value); /* 8 */
            }

            @else {
                  @warn 'There is no unit conversion for #{$unit}'; /* 9 */
            }

      }

      @return $list(); /* 10 */

}

它是如何工作的?

变量$px-only不仅仅是属于main.scssmain-ie.scss文件内。所有导入的文件都将影响$px-only的值。当u()函数加载时,main.scss或者main-ie.scss都指定了变量$px-only的值。为了其他模块能正常工作,必须先导入这个Sass函数。

函数的解释

  1. 这个变量是指默认浏览器的字体大小和1rem = 16px。为了更好的维护,我们将此值存储在一个变量中,以便重用和容易修改。
  2. 这个函数返回一个数字的这符串,此函数由Hugo提供
  3. 我们称为函数u,因为它更容易输入。u也代表单位。这个函数接受一个参数,一个rem或者px值的数组。
  4. 我们定义一个新的列表,将保存新值。
  5. $valuse参数里的每个$value值,通过测试当前的$value值并将其存储在$list数组内。
  6. 这个Sass函数返回一个带单位的数字。在我们的例子中,是px或者rem
  7. 首要条件:如果当前模式是像素模式和单位的值为rem,那么rem值将转换为px并把它们放到$list数组中。
  8. 第二个条件:如果单位是px或rem,我们只是将值入到列表中。
  9. 如果第一个和第二个条件失败,我们在Sass控制台上输入一个警告信息。
  10. 最后,我们返回整个简洁的CSS值列表。

使用

_module-example.scss

.main-header {
 margin:u(1rem 2rem 20px 3rem);
 padding-bottom:u(0.25rem);
 font-size:u(0.875rem);
}

转译出来的CSS

main.css

.main-header {
 margin: 1rem 2rem 20px 3rem;
 padding-bottom: 0.25rem;
 font-size: 0.875rem;
}

main-ie.csss

.main-header {
 margin: 16px 32px 20px 48px;
 padding-bottom: 4px;
 font-size: 14px;
}

优点和缺点

优点

  • 易于维护:一旦编辑代码,将生成两个不同的样式表
  • 容易阅读:你最后的CSS是干净的,没有未使用的属性
  • 降低文件大小:你最好的CSS是轻量级的
  • 快速开发:快速输入u()来生成最终需要的单位

缺点

  • 需要在<head>内使用条件样式引入正确的样式表

我希望得到您的反馈和分享你的任何想法,哪些地方可以改进!

扩展阅读

原文链接:https://www.w3cplus.com/preprocessor/rem-px-browser-function-sass.html

发表评论

登录后才能评论