前端的一个方案,混合rem和非rem

一、什么是rem

Hybrid 的H5 需求,一般当我们面向移动端,会考虑rem布局,或者 其他等效的布局。布局的原理就是使用一个相对单位,把书写的px通过 插件编译为相对单位,比如rem。 这样当你动态的设置 rem,整个页面的尺寸都会随之变化。 rem的布局特点是等比缩放,通过等比缩放来兼容各个尺寸的手机屏幕。

参考:使用Flexible实现手淘H5页面的终端适配

二、rem的问题

rem的优点就是他的缺点,他通过等比缩放来实现兼容。这时候会存在一个问题。 大屏幕和小屏幕看到的内容是一样的,不会因为屏幕变大文字增多。当然你可以把字体使用 绝对单位来书写。

rem的问题在于当我们希望 H5可以让手机和 iPad这样的中屏幕设备共用的时候,问题就会暴露出来。一些元素会变得巨大无比。显然站在这个角度是无法接受的。

三、混合方案

1.配置rem的方案

关于rem 推荐的方式是 监控屏幕,动态的调整 rem大小。比如如下这段代码,可以加入。在 index.html。这里把屏幕宽度 除以10,也就是平均分成10份。

<script>
  (function (doc, win) {
    const docEl = doc.documentElement;
    const resizeEvt = 'onorientationchange' in window ? 'onorientationchange' : 'resize';
    const recalc = function () {
      let clientWidth = docEl.clientWidth;
      clientWidth = clientWidth < 750 ? clientWidth : 750;
      docEl.style.fontSize = (clientWidth / 10) + 'px';
    };
 
    if (!doc.addEventListener) return;
    win.addEventListener(resizeEvt, recalc, false);
    doc.addEventListener('DOMContentLoaded', recalc, false);
  })(document, window);
</script>

配合上 rem的编译方案,可以使用 postCSS插件 postcss-plugin-px2rem。 这里推荐的postCSS插件的原因是,实现同样的效果,可以引入sass的函数,来处理计算。但是不够优雅。因为你的代码中px必然要替换成某种函数 px2rem(12px)。这种方案在rem还在朦胧的初期是被广泛使用的。这样会污染你的源代码。今天不推荐。

使用插件,把转换过程留给编译期间。

postcss-plugin-px2rem 可以在 webpack 的loader中像这样配置:

  {
 
    loader: require.resolve('postcss-loader'),
    options: {
      // Necessary for external CSS imports to work
      // https://github.com/facebook/create-react-app/issues/2677
      ident: 'postcss',
      plugins: () => [
        require('postcss-flexbugs-fixes'),
        require('postcss-preset-env')({
          autoprefixer: {
            flexbox: 'no-2009',
          },
          stage: 3,
        }),
        // Adds PostCSS Normalize as the reset css with default options,
        // so that it honors browserslist config in package.json
        // which in turn let's users customize the target behavior as per their needs.
        postcssNormalize(),
        require('postcss-plugin-px2rem')({
          rootValue: 41.4,
          minPixelValue: 12,
          exclude: /(node_modules|styles-skip-px2rem|styles_skip_px2rem)/
        })
      ],
      sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
    },
  },
]

其中比较关键的是,这里是 postcss-plugin-px2rem

require('postcss-plugin-px2rem')({
  rootValue: 41.4,
  minPixelValue: 12,
  exclude: /(node_modules|styles-skip-px2rem|styles_skip_px2rem)/
})

我们前面把跟字体设置成 屏幕宽度的10分之一,就是把屏幕平均分成了十份。

这里 rootValue 是一个自己设置的值。这里参考你要对应的设计图标准。这里是414px的设计稿,平均分成十份就是41.4 那么根值就是41.4。

当你按照414px的设计稿进行标注样式的时候,就会可以页面产生放缩效果。

2.混合方案

我遇到的问题是,我们的需求是混合的,一些页面是活动页面,页面设计依赖绝对定位,于是等比放缩是一个非常好的方案。 但是一些业务页面,他需要H5和iPad共用。为了使他们不要太过夸张,整个的书写方案,要采用 bootstrap那种,媒体查询+绝对单位来书写。通过自适应宽来解决问题。

上面得代码中 起到了关键作用。

exclude: /(node_modules|styles-skip-px2rem|styles_skip_px2rem)/

我个人觉得,这目前算是比较优雅的方案。

在一些临时的跳过 px2rem 可以使用 PX,因为 px2rem只会读取小写px,而PX浏览器也能接受。 但是如果你需要一个页面都跳过 px2rem 全部书写 PX无疑也是一种污染,并且在实际工作中,你无法利用figma等设计工具的粘贴值。

这里可以使用 exclude 。 “约定大于配置”,我们可以在项目代码中,建立一个 特征styles文件夹,这里我定义为styles-skip-px2rem 名字可读性很好。这里的样式将会跳过 px2rem, 我们的源代码里,将会是px。

源代码杜绝了污染。

有趣的地方来了,如果我们想要突然兼容rem了。我们只需要把 css,scss 从特征文件夹移出来,修改样式的地址引用。这样px又会被重新编译成rem。 理论上我们前期足够的考虑。我们的代码可以简单修改就可以继续复用。

混合方案归功于插件提供了丰富的配置。这里使用 execute 排除约定文件内容,实现混合。保持源码远离污染。

3.混合方案的好处

页面可以局部 等比放缩,也可以绝对像素。 更理想的方案应该如此,比如文字使用绝对单位,大屏幕可以看的更多。而头图Banner 可以使用等比放缩。

如果再去观察手淘、京东。他们已经从一个完全rem的方案中,逐渐变成了 rem混合媒体查询的混合体了。 我不清楚他们用的什么方案,但这个方案可以考虑。

四、rem vs px 的讨论

关于 rem和px的论战有很多,比如 知乎:为什么很多web项目还是使用 px,而不是 rem?

是的,这就是对一个问题,不同的公司立足业务场景,他们取舍不同。有的公司希望百分百被还原设计图,不接受任何位移。这样rem就是优秀的方案。 比如我们的设计师不在意百分百还原,他更看重,H5和宽屏设备可以共享页面,这样rem就不满足需求,媒体查询响应式更合适。 但是我们讨论之后,也觉得一些元素也可以等比放缩。甚至一些关于定位展示很强的需求,rem依然是好的方案。

这套混合的小方案,给大家参考下。

个人观点:技术方案不必拘泥,我甚至信丰万物皆可MIX。MIX才会产生新事物。

这个方案,可以说的亮点,我觉得就是,可以在 —— 不污染源代码的前提下,可以提供一个页面混合 rem和px。

可以解放设计师也可以解放前端,尤其是他的开发过程是优雅的,不必污染源代码,figma依然可以粘贴设计师的有效值。

Mark24

Everything can Mix.