其实这道题是一个简单的题,但是我当时回答的并不好,所以在此记录一下
核心回答框架
- 基础概念说明
-
REM 适配的本质是通过
动态调整根元素的字体大小来实现页面元素的等比缩放
,我们通过会将设计稿尺寸与REM单位建立可计算的映射关系。我们通常会将基础的html的font-size 设置成100px 或 50px -
移动端设计稿一般是750,100px 对应就是1rem
inifont-size = 设备宽度 * 100 / 750
js
// 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的转换
需要适配的阴影效果可以使用媒体查询单独调整
js
.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 相关完整配置
js
// 进阶配置方案 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 集成
js
// 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
js
@function rem($px) {
@return ($px / 100) + rem;
}
/* 使用示例 */
.title {
font-size: rem(40); // 40px -> 0.4rem
}
多主题的实现很REM 很像
js
// 读取全部主题配置
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 的功能