移动端Web适配方案实战总结从rem到vw的踩坑经历

楚恒 Dev 框架 阅读 2,126
赞 8 收藏
二维码
手机扫码查看
反馈

先说说为啥要搞Web适配

做移动端开发,适配这个问题真的是绕不过去的大山。我之前接手一个项目,设计师给的设计稿是375px宽的,结果测试同学一测,iPhone 12显示正常,安卓各种机型都乱套了。这就是典型的适配没做好,当时真是被折磨得够呛。

移动端Web适配方案实战总结从rem到vw的踩坑经历

现在回过头来看,Web适配主要解决的是不同设备屏幕大小差异带来的布局问题。iPhone的375px,在某些安卓机上可能就是414px或者更大,如果不处理的话,页面就会拉伸变形。

rem方案,我的首选

我一般都用rem来做适配,这个方案比较成熟稳定。核心思想就是根据根元素的font-size来计算其他元素的尺寸。比如设计稿宽度375px,我把根字体设为37.5px,那1rem就等于10px,这样换算起来就比较直观。

先看看核心代码:

function setRem() {
    const docEl = document.documentElement;
    const clientWidth = docEl.clientWidth;
    if (clientWidth >= 750) {
        docEl.style.fontSize = '100px';
    } else {
        docEl.style.fontSize = clientWidth / 7.5 + 'px';
    }
}
setRem();
window.addEventListener('resize', setRem);

这里有个需要注意的地方:为什么要除以7.5?因为设计稿是750px(2倍图),除以7.5正好是100px,方便计算。如果设计稿是375px的,那就除以3.75。

然后CSS里就直接用rem:

.box {
    width: 37.5rem; /* 对应设计稿375px */
    height: 20rem; /* 对应设计稿200px */
    font-size: 1.4rem; /* 对应设计稿14px */
}

vw方案也挺香

vw这个单位也越来越流行了,1vw等于视口宽度的1%。用它来做适配其实更简单,不用写JS代码。

假设设计稿375px宽,那1px就等于100vw/375=0.2667vw。我们可以用CSS预处理器来自动转换:

$design-width: 375;

@function px2vw($px) {
    @return $px / $design-width * 100vw;
}

.box {
    width: px2vw(375); // 100vw
    height: px2vw(200); // 53.33vw
    font-size: px2vw(14); // 3.73vw
}

如果你不想用预处理器,也可以直接计算。比如375px就写成100vw,200px就是53.33vw,这样换算就行。

踩坑提醒:这些地方容易出错

做过一段时间后发现几个特别容易踩坑的地方:

  • border 1px的问题:在高清屏上,1px可能看起来很粗。可以用transform: scale(0.5)来处理
  • 图片适配:记得给img设置max-width: 100%,避免溢出容器
  • 字体不要用rem:字体用px或em更好,用rem在不同设备上可能会出现字体过小或过大的问题

特别是border 1px这个问题,我之前调试了好久才发现原因。现在的做法是:

.border-1px {
    position: relative;
}
.border-1px::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 1px;
    background: #ddd;
    transform: scaleY(0.5);
    transform-origin: 0 0;
}

混合方案,取长补短

有些场景单纯用rem或vw都不太合适,我会结合使用。比如大屏展示类的页面,用vw做整体布局,用rem控制细节。

还有一种情况,就是需要适配刘海屏、挖孔屏这种异形屏。这时候就需要配合CSS env()函数:

.safe-area {
    padding-top: env(safe-area-inset-top);
    padding-bottom: env(safe-area-inset-bottom);
}

这个函数能获取到安全区域的边距,避免内容被刘海遮挡。

工具辅助,解放双手

手动换算px到rem或vw太麻烦了,我一般会用PostCSS插件来自动转换。

安装postcss-pxtorem:

npm install postcss-pxtorem --save-dev

配置webpack:

module.exports = {
    plugins: [
        require('postcss-pxtorem')({
            rootValue: 37.5, // 设计稿宽度/10
            propList: ['*'],
            selectorBlackList: ['.no-rem']
        })
    ]
}

这样写CSS的时候直接用px,构建时会自动转成rem。需要注意的是,rootValue的值要跟JS中设置的根字体大小保持一致。

性能优化小技巧

适配方案虽然解决了兼容性问题,但也带来了一些性能考虑。特别是rem方案,频繁的resize监听可能影响性能。

优化方法:

  • 防抖处理resize事件,不要让setRem频繁执行
  • 对于不需要动态调整的元素,可以用固定的rem值
  • 考虑用CSS媒体查询处理一些固定断点的情况

防抖版本的代码:

let timeoutId;
function debounceSetRem() {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(setRem, 100);
}
window.addEventListener('resize', debounceSetRem);

最后的小结

Web适配这东西说复杂也复杂,说简单也简单。关键是要根据项目需求选择合适的方案。我现在一般推荐rem+postcss的组合,稳定可靠,维护成本也不高。

当然,这几种方案都有各自适用的场景,vw适合简单的响应式布局,rem适合精细化的像素级还原。最重要的是要考虑团队的技术栈和维护成本。

以上是我踩坑后的总结,希望对你有帮助。这个技巧的拓展用法还有很多,后续会继续分享这类博客。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论