面试官:项目中rem是怎么使用的,你们一般设置多少?

0 阅读2分钟

其实这道题是一个简单的题,但是我当时回答的并不好,所以在此记录一下

核心回答框架

  1. 基础概念说明
  • REM 适配的本质是通过动态调整根元素的字体大小来实现页面元素的等比缩放,我们通过会将设计稿尺寸与REM单位建立可计算的映射关系。我们通常会将基础的html的font-size 设置成100px 或 50px

  • 移动端设计稿一般是750,100px 对应就是1rem

        font-size = 设备宽度 * 100 / 750
        
    
// flexible.js
(function () {
  function setRem() {
    const baseSize = 100; // 基础 `font-size`
    const designWidth = 750; // 设计稿宽度
    const scale = document.documentElement.clientWidth / designWidth;
    document.documentElement.style.fontSize = baseSize * scale + 'px';
  }
  setRem();
  window.addEventListener('resize', setRem);
})();


高级补充点(展现深度)

PostCSS 插件实现自动转换

我们喜欢设计稿是什么,直接CV过来,但是每次需要手动改动尺寸,然后我就引入postcss-pxtorem插件,让插件帮我们去实现转换这个过程。但是有一些特殊情况,在移动端1px 不同设备上渲染会出现不一致的粗细表现,比如border/box-shadow 那我们就要配置不去进行rem的转换

需要适配的阴影效果可以使用媒体查询单独调整

.card {
  box-shadow: 2px 4px 10px rgba(0,0,0,0.1);
  
  @media (max-width: 375px) {
    box-shadow: 1px 2px 5px rgba(0,0,0,0.1);
  }
}

postCSS 相关完整配置

// 进阶配置方案 postcss.config.js
module.exports = {
  plugins: {
    'postcss-import': {},
    'postcss-url': {},
    'postcss-preset-env': {
      browsers: 'last 2 versions',
      stage: 3,
      features: {
        'nesting-rules': true
      }
    },
    'postcss-pxtorem': {
      rootValue: 100,
      propList: [
        '*',
        '!border*', // 边框默认不转换
        '!box-shadow' // 阴影默认不转换
      ],
      selectorBlackList: [
        /^html$/,
        /^body$/,
        /^no-rem/
      ],
      mediaQuery: true,
      exclude: /node_modules/i
    },
    cssnano: {}
  }
}

Webpack 集成

// webpack.config.js
module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          'style-loader',
          'css-loader',
          {
            loader: 'postcss-loader',
            options: {
              postcssOptions: require('./postcss.config.js')
            }
          }
        ]
      }
    ]
  }
}

结合 scss / less 进行 rem 适配

如果项目里用了scss / less 完全可以不用postCSS

@function rem($px) {
  @return ($px / 100) + rem;
}

/* 使用示例 */
.title {
  font-size: rem(40); // 40px -> 0.4rem
}

多主题的实现很REM 很像

// 读取全部主题配置
const themeFileList: Record<string, string> = import.meta.glob(
  ['@/generated/theme/*.ts', '!@/generated/theme/theme-enum.ts'],
  {
    import: 'default',
    eager: true
  }
)
// 根据读取的文件路径,生成名称和地址
const themeFileListObject = {}
for (const key in themeFileList) {
  const filename = key.split('/').pop()?.replace('.ts', '')
  if (filename) {
    themeFileListObject[filename] = themeFileList[key]
  }
}

// 替换root样式
const injectRootStyle = (theme: ThemeEnum) => {
  const themeObject = themeFileListObject[theme]
  for (const key in themeObject) {
    document.documentElement.style.setProperty(key, themeObject[key])
  }
}

总结

以上就是我对REM 相关概念及实际应用的总结,从REM的基本概念以及换算公式,从而引入自动化css 单位转换,提供postcss-pxtorem 方案,并且解决1px 换算成0.01 像素在移动端展示粗细不一致的问题。并提供了完整的工程化配置

另外如果使用scss/less, 也可自定义方法函数实现这一转换

最后由动态font-size 又引到自己实现过的动态主题切换,殊途同归,都是利用 document.documentElement.style.xxx 去实现动态设置size / color 的功能