# rem

说rem之前先说一下em。这两个都是相对长度单位

em在 font-size 中使用时是相对于父元素的字体大小,在其他属性中使用是相对于自身的字体大小,如 width。我们常用的段落首行缩进2个字体大小就是用的text-indent: 2em;

概括地说,在排版属性中 em 单位的意思是“父元素的字体大小”。而rem 单位的意思是“根元素(html)的字体大小,即(root em)

# 1、长度计算逻辑

<html lang="en" style="font-size: 37.5px;">
<style>
    .box {
        width: 2rem;
    }
</style>

比如上面的例子,box的实际宽度就是 37.5 * 2 = 75px。以根元素的字体大小为相对值,节点本身的rem值为系数。

rem平时主要用在移动端的适配中,说两种实践的方式吧。

以下的例子都是建立在设计稿是375宽度的基础之上

# 2、自己计算rem值

为了方便我们自己计算rem的值。我们可以把rem设置为

var rem = document.documentElement.clientWidth / 375 * 100

这样子设置的最大好处就是非常方便计算。表达式里的375就是我们设计稿的宽度。比如设计稿里面元素的大小事20px。那么我们只需要除以100,设置为0.2rem即可。缺点就是需要我们手动计算一次。还有一个缺点就是根元素的字体大小会设置的比较大。 以实际设备宽度为375px来看,根元素的字体大小就是100px,此时为了防止这个属性被其他元素继承到,导致出现很大的文字,需要适当的给body设置一个合适的font-size。

以设计稿宽度为375举例

设备宽度 html rem值 设计稿尺寸 实际设置值 实际渲染尺寸
320 85.333px 50px 0.5rem 42.666px
375 100px 50px 0.5rem 50px
390 104px 50px 0.5rem 52px
414 110.4px 50px 0.5rem 55.2px

由上面的表格可以看到,同样的设计稿尺寸50px。除以100之后 设置元素的实际代码尺寸为0.5rem。实际渲染的尺寸就可以随着屏幕的宽度变化都变化了。

提供一个设置rem的代码片段

(function (win, uiWidth) {
  var docEl = win.document.documentElement;
  var _rem = {
    rem: 0
  };
  var timer = null;
  function resize() {
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }
    timer = setTimeout(() => {
      var width = docEl.clientWidth;
      var rem = (width / uiWidth) * 100;
      docEl.style.fontSize = rem + 'px';
      _rem.rem = rem;
      //误差、兼容性处理
      var actualSize = parseFloat(window.getComputedStyle(docEl)['font-size']);
      if (actualSize !== rem && actualSize > 0 && Math.abs(actualSize - rem) > 1) {
        var remScaled = (rem * rem) / actualSize;
        docEl.style.fontSize = remScaled + 'px';
        _rem.rem = remScaled;
      }
    }, 50);
  }
  resize();

  //窗口更新动态改变font-size
  win.addEventListener('resize', resize, false);

  win.addEventListener(
    'pageshow',
    function (e) {
      if (e.persisted) {
        resize();
      }
    },
    false
  );

  _rem.resize = resize;
  _rem.rem2px = function (remVal) {
    var pxVal = parseFloat(remVal) * this.rem;
    if (typeof remVal === 'string' && remVal.match(/rem$/)) {
      pxVal += 'px';
    }
    return pxVal;
  };
  _rem.px2rem = function (pxVal) {
    var remVal = parseFloat(pxVal) / this.rem;
    if (typeof pxVal === 'string' && pxVal.match(/px$/)) {
      remVal += 'rem';
    }
    return remVal;
  };
  // 主要用于一些通过js设置样式的代码, 暴露一个全局的对象
  win._rem = _rem;
})(window, 375);

# 3、postcss-pxtorem

postcss-pxtorem (opens new window)。如果工程上有打包工具, 那么就可以使用这个postcss的插件了。

使用这个插件的好处就是不用自己计算和书写rem了,统一在编译的时候搞定。举个例子, 比如375宽度的设计稿,元素设计稿尺寸是多少,就写多少, 编译之后会自动转换px单位到rem单位。

如果是webpack之类的工具, 提供一个插件配置做参考

postcss.config.js

module.exports = {
  plugins: {
    'postcss-pxtorem': {
      rootValue: 37.5,
      unitPrecision: 3,
      minPixelValue: 2,
      propList: ['*']
    }
  }
};

如果设计稿是375的宽度,可以设置rootValue为37.5,于此 同时html元素的font-size设置,也不能采用上面第二节的方式, rootValue 可以理解为设计稿尺寸下根元素的字体大小。而是应该采用下面的方式

html.style.fontSize = html.clientWidth / 10 + 'px'

设计稿尺寸、pxtorem插件配置、根元素字体大小设置三者需要对应上。满足下面的逻辑

pxtorem.rootValue = 设计稿尺寸 / 10 
根元素字体大小 = html.clientWidth / 10

这个10 采用的是一个经验值, 当时设置成其他的系数也是可以的, 只不过保持一致即可。

如果不想让px转换为rem。直接把px写成Px或者PX这种大写的即可。

提供一段设置rem的代码片段

!(function (win) {
  var doc = window.document;
  var html = doc.documentElement;
  var ratio = win.devicePixelRatio || 1;
  function onload() {
    if (doc.body) {
      doc.body.style.fontSize = 12 * ratio + 'px';
    } else {
      doc.addEventListener('DOMContentLoaded', onload);
    }
  }

  function resize() {
    const val = html.clientWidth / 10;
    html.style.fontSize = val + 'px';
  }
  onload();
  resize();
  win.addEventListener('resize', resize);
  win.addEventListener('pageshow', function (e) {
    // 如果网页来自缓存
    e.persisted && onload();
  });
})(window);

上次更新: 1/22/2025, 9:39:13 AM