rem适配方案

153 阅读3分钟

前言

rem(root em,根em)是 CSS3 新增的一个相对单位, 表示相对于根元素(html)的字体大小。这个单位与 em 的区别在于使用 rem 相对的是 HTML根元素

这个单位可谓集相对大小和绝对大小的优点于一身,通过它既可以做到只修改根元素就成比例地调整所有尺寸大小,又可以避免逐层复合的连锁反应。目前,除了IE8及更早版本外,所有浏览器均已支持rem。

因此,使用 rem 单位可以让开发者更容易地实现响应式设计,因为我们只需要关注根元素的font-size。

举个简单的例子:设置字体大小和边距

<!DOCTYPE html>
<html>
  <head>
    <style>
      /* 设置根元素的字体大小 */
      html {
        font-size: 16px;
      } 
      /* 使用rem单位设置一个元素的字体大小 */
      h1 {
        font-size: 2rem;
      } 
      /* 使用rem单位设置一个元素的边距 */
      p {
        margin-bottom: 1rem;
      }
    </style>
  </head>
  <body>
    <h1>标题</h1>
    <p>内容</p>
    <p>内容</p>
  </body>
</html>

注意:使用<!DOCTYPE html>声明为标准模式,防止浏览器在渲染文档时,切换到我们称为“怪异模式”的渲染模式,在怪异模式下样式继承会出现问题

在这个示例中,我们设置了根元素(html)的字体大小为 16px。然后,我们使用 rem 单位设置了标题(h1)的字体大小为 2rem,这等于 32px(2 * 16)。

我们还为段落(p)设置了 1rem 的底部边距,等于 16px(1 * 16)。我们想让整个页面等比变化,只需要更改根元素的 font-size 属性即可,如

html { 
    font-size: 20px; 
}

基于此,我们在页面初始化的时候,初始化根元素的 font-size 值,监听 onresize 事件在窗口大小变化时,根据比例动态计算出根元素的 font-size 值,那么就能确保网站在不同设备上都能呈现出最佳的效果。

const getScreenWidth = () => window.innerWidth || window.screen.availWidth || window.screen.width;
const setRootFontSize = () => {
  const designWidth = 375; // 设计稿宽度
  const minWidth = 360;
  const maxWidth = 480;
  const baseFontSize = 16;

  const windowWidth = getScreenWidth();
  let adaptiveWidth;
  if (windowWidth <= minWidth) {
    adaptiveWidth = minWidth;
  } else if (windowWidth >= maxWidth) {
    adaptiveWidth = maxWidth;
  } else {
    adaptiveWidth = windowWidth;
  }

  const fontSize = baseFontSize * (adaptiveWidth / designWidth);
  // 保留3位小数
  document.documentElement.style.fontSize = `${toFixedBySelf(fontSize, 3)}px`;
};

// 初始化根元素字体大小
setRootFontSize();

// 改变窗口时重新设置
window.onresize = throttle(() => {
  setRootFontSize();
}, 200);

我们可以借助 webpack 工具 postcss-pxtorem 来完成单位的自动转化,因此开发时正常使用px即可。

在项目根目录添加 postcss.config.ts文件

module.exports = {
  plugins: {
    'postcss-pxtorem': {
      rootValue: 16, // 基准值
      unitPrecision: 5, // 转换后的 rem 值保留的小数位数
      propList: ['*'],
      selectorBlackList: [],
      replace: true, // 是否直接替换属性值,而不是添加备用属性。
      mediaQuery: false, // 是否允许在媒体查询中转换 px。
      minPixelValue: 0, // 设置要替换的最小像素值。
    },
  },
};

某些场景下,我们可能不需要进行单位的转化,因此 postcss-pxtorem 配置项为我们提供了可以避免px转rem的实现方法:

1、propList:允许从 px 更改为 rem 的属性列表

propList: ['*', '!border*'] // 如:不转换任何包含 "border" 的属性

2、selectorBlackList:选择器黑名单,如果选择器中包含这些字符串,则不转换对应的 px

selectorBlackList: ['html', '.no-rem'] // 如:不转换 "html" 标签和包含 "no-rem" 类的选择器

注意:在内联样式中编写样式属性时,我们需要手动将单位转换为rem

function pxToRem(value) { 
  return `${value / 16}rem`; 
}

注意,打包时 postcss-pxtorem 只会将样式文件中的单位进行自动转换,而我们写在内联样式不会进行处理,故需要手动进行单位的转换。

总结

优点:

  • 视觉一致性:rem 是相对于根元素(html)的字体大小来计算的,这使得整个页面的尺寸可以根据根元素的字体大小进行缩放,从而实现响应式设计。
  • 易于维护:通过调整根元素的字体大小,可以一次性调整整个页面的尺寸,而不需要逐个修改每个元素的样式,简化了维护成本。
  • 灵活性:可以根据不同的屏幕尺寸动态调整根元素的字体大小,从而适应不同的设备,提供更好的用户体验。
  • 兼容性:rem 单位在现代浏览器中得到了广泛支持,包括移动设备和桌面设备,兼容性较好。

缺点:

  • 初始设置复杂:需要设置根元素的字体大小、添加配置文件。
  • 依赖JavaScript:为了实现动态调整根元素字体大小,通常需要使用JavaScript来监听窗口大小的变化并相应地调整根元素的字体大小。
  • 内容展示区域:屏幕较大的设备等比放大后一屏能展示的内容会变少。