ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Vue3大屏适配全攻略:从移动端到带鱼屏

Vue3大屏适配全攻略:从移动端到带鱼屏 博主介绍‍ 了解博主波仔椿 人生箴言组件拆得够小页面才装得下变化。 我的专栏Vue3-实战派文章内容上个月接了个数据大屏的活设计稿是 1920×1080客户现场的屏幕却是 2560×1440 的带鱼屏一打开页面元素全挤在左上角右边一大片空白。改了一晚上适配才搞定。今天把 Vue3 项目里屏幕适配的那些坑和方案一次性拆给你看从移动端到大屏全覆盖。一、屏幕适配到底在解决什么问题很多人一上来就问用 rem 还是 vw但连适配要解决什么都没搞清楚。先把问题盘清楚方案选型才不会瞎。1. CSS 像素和物理像素不是一回事你在 CSS 里写width: 375px在 iPhone 12 上实际渲染的物理像素是 1170px——因为它的设备像素比DPR是 3。说白了CSS 像素是逻辑单位物理像素才是屏幕上真正发光的点DPR 就是两者之间的换算系数。DPR 3DPR 2控制映射关系CSS 逻辑像素 375px物理像素 1170px物理像素 750pxviewport 元标签这就是为什么同样写1px的边框在 Retina 屏上看起来比设计稿粗——CSS 的 1px 对应了 2 个甚至 3 个物理像素。你发现没有适配的本质不是让 px 变小而是让逻辑尺寸和物理屏幕之间建立一套可伸缩的映射关系。2. 那些年踩过的适配坑我把项目里最容易翻车的点列了张表你可以对照排查问题表现根因1px 边框变粗Retina 屏上线条发虚、偏粗CSS 1px 对应多个物理像素刘海屏内容被挡顶部按钮被灵动岛遮住未处理安全区域safe-area图片模糊高清屏上图片发虚用了 1 倍图没按 DPR 提供 2x/3x 图横竖屏错乱旋转后布局崩坏未监听orientationchange字体忽大忽小部分机型字体不跟随缩放iOS 文本自动调整-webkit-text-size-adjust行内样式没被转换用了 postcss 插件但部分 px 没变行内 style 不经过 PostCSS 处理大屏元素错位非标准分辨率下布局散架固定 px 布局没有缩放层这些问题不是某个方案能全部解决的得组合着来。下面逐个方案讲清楚各自的适用场景。二、方案一rem 动态根字号经典移动端方案rem 的原理很简单1rem等于根元素html的font-size。只要根据屏幕宽度动态改变根字号所有用 rem 写的尺寸就会跟着等比缩放。1. 用 Vue3 组合式函数实现 rem 适配不用再引lib-flexible那种老库了Vue3 里一个 composable 就能搞定// src/composables/useRem.jsimport{onMounted,onUnmounted}fromvue/** * 移动端 rem 适配 * param {number} designWidth 设计稿宽度默认 375 * param {number} maxWidth 最大限制宽度防止 PC 端字号过大 */exportfunctionuseRem(designWidth375,maxWidth750){functionsetRem(){constclientWidthdocument.documentElement.clientWidth||window.innerWidth// 限制最大宽度PC 端打开不至于字大到离谱constwidthMath.min(clientWidth,maxWidth)// 把设计稿分成 10 份1rem 设计稿宽度/10// 比如 375 设计稿 → 1rem 37.5px元素 75px 就写 2remconstfontSize(width/designWidth)*(designWidth/10)document.documentElement.style.fontSizefontSizepx}onMounted((){setRem()window.addEventListener(resize,setRem)window.addEventListener(orientationchange,setRem)})onUnmounted((){window.removeEventListener(resize,setRem)window.removeEventListener(orientationchange,setRem)})}在App.vue里调用一次就行!-- src/App.vue --scriptsetupimport{useRem}from./composables/useRem// 设计稿 375PC 端最大限制到 750useRem(375,750)/scripttemplatedivclassapprouter-view//div/template2. 配合 postcss-pxtorem 自动换算手写 rem 太费脑子让 PostCSS 帮你把 px 自动转成 remnpminstallpostcss-pxtorem-D// postcss.config.jsexportdefault{plugins:{postcss-pxtorem:{rootValue:37.5,// 设计稿宽度 / 10和 useRem 里的基准保持一致propList:[*],// 所有属性都转换selectorBlackList:[.ignore-px],// 带这个类名的不转换minPixelValue:2,// 小于 2px 的不转1px 边框保留原样mediaQuery:false// 媒体查询里的 px 不转换}}}这样你在样式里照常写width: 100px构建后自动变成width: 2.66667rem。说白了rem 方案的核心就两步——JS 动态改根字号 PostCSS 自动转单位。少了哪一步都得手算。3. rem 方案的局限依赖 JS根字号要靠 JS 算首屏如果 JS 加载慢会有一瞬间字号不对导致布局闪烁字体问题rem 是等比缩放但有些场景字体不希望跟着缩比如正文可读性得单独处理大屏不适用rem 是按宽度缩放的大屏项目高度方向容易出问题三、方案二vw/vh postcss-px-to-viewport现代主流方案vw 是视口宽度的百分之一100vw就是整个屏幕宽。它是纯 CSS 单位不依赖 JS浏览器原生支持现在已经是移动端适配的首选。1. Vite 中的配置Vue3 Vite 项目里直接在vite.config.js配置 PostCSS 插件npminstallpostcss-px-to-viewport-D// vite.config.jsimport{defineConfig}fromviteimportvuefromvitejs/plugin-vueimportpostcssPxToViewportfrompostcss-px-to-viewportexportdefaultdefineConfig({plugins:[vue()],css:{postcss:{plugins:[postcssPxToViewport({unitToConvert:px,// 要转换的单位viewportWidth:375,// 设计稿宽度unitPrecision:5,// 转换后保留的小数位propList:[*],// 所有属性都转viewportUnit:vw,// 转换成 vwfontViewportUnit:vw,// 字体也转 vwselectorBlackList:[.ignore],// 忽略的选择器minPixelValue:1,// 最小转换值mediaQuery:false,// 媒体查询不转换replace:true,// 直接替换而不是添加备用exclude:[/node_modules/]// 排除第三方库})]}}})配完之后你写的width: 100px会被编译成width: 26.66667vw100 / 375 × 100。2. 移动端和 PC 端双设计稿切换如果项目同时要适配移动端375和 PC 端1920可以通过环境变量切换viewportWidth// vite.config.js节选postcssPxToViewport({viewportWidth:process.env.MODEmobile?375:1920,// ...其他配置同上})3. vw 方案要注意的坑坑一行内样式不会被转换。PostCSS 只处理style里的样式模板里的:style{ width: 100px }不会被转。解决办法是把需要适配的尺寸写到 CSS 类里或者用 CSS 变量做桥接!-- src/components/Card.vue --scriptsetup// 动态尺寸用 CSS 变量传递样式里用 calc 配合 vwconstcardWidth26.667vw// 等价于 100px 375 设计稿/scripttemplatedivclasscard:style{ --card-w: cardWidth }卡片内容/div/templatestylescoped.card{width:var(--card-w);}/style坑二1px 边框不要转 vw。1px转成0.267vw在某些屏幕上可能渲染不出来。用minPixelValue: 1排除掉或者给边框加.ignore类名。坑三100vw在移动端包含滚动条宽度会导致横向溢出。用100%代替100vw做容器宽度或者设置overflow-x: hidden。有意思的地方就在这儿vw 看起来比 rem 简单但真正的坑都在哪些 px 不该转上——边框、行内样式、第三方组件这些都得单独拎出来处理。四、方案三transform: scale 整体缩放大屏 / 固定设计稿首选数据大屏、活动页这种设计稿固定通常 1920×1080、不希望布局被打散的场景最稳的方案是把整个页面用transform: scale()包一层缩放。1. Vue3 组合式函数实现等比缩放// src/composables/useScaleScreen.jsimport{ref,onMounted,onUnmounted}fromvue/** * 大屏等比缩放适配 * param {number} designWidth 设计稿宽度默认 1920 * param {number} designHeight 设计稿高度默认 1080 */exportfunctionuseScaleScreen(designWidth1920,designHeight1080){consttransformref(scale(1) translate(-50%, -50%))functionupdateScale(){constscaleXwindow.innerWidth/designWidthconstscaleYwindow.innerHeight/designHeight// 取较小值保证完整显示不留白也不裁切constscaleMath.min(scaleX,scaleY)transform.valuetranslate(-50%, -50%) scale(${scale})}onMounted((){updateScale()window.addEventListener(resize,updateScale)})onUnmounted((){window.removeEventListener(resize,updateScale)})return{transform,designWidth,designHeight}}2. 在大屏页面中使用!-- src/views/Screen.vue --scriptsetupimport{useScaleScreen}from../composables/useScaleScreenconst{transform,designWidth,designHeight}useScaleScreen(1920,1080)/scripttemplate!-- 外层占满整个屏幕作为缩放的舞台 --divclassscreen-stage!-- 内层固定设计稿尺寸通过 transform 缩放居中 --divclassscreen-content:style{ width: designWidth px, height: designHeight px, transform }!-- 这里面所有元素都按 1920×1080 写死 px 就行 --headerclassscreen-header数据可视化大屏/headermainclassscreen-mainsectionclasspanel面板一/sectionsectionclasspanel面板二/section/main/div/div/templatestylescoped.screen-stage{width:100vw;height:100vh;overflow:hidden;background:#0a1929;}.screen-content{position:absolute;top:50%;left:50%;transform-origin:center center;}/stylescaleX 2560/1920 1.333scaleY 1440/1080 1.333窗口尺寸 2560×1440计算缩放比取较小值scale 1.333transform: scale 1.333 居中设计稿 1920×1080 完整铺满3. scale 方案的坑和解法坑一ECharts 图表不跟着 resize。缩放后图表容器的 CSS 尺寸没变还是 1920×1080ECharts 内部不知道要重绘。需要在缩放后手动调用chart.resize()或者监听window.resize事件统一触发// src/composables/useEchartsResize.js节选import{onMounted,onUnmounted}fromvueexportfunctionuseEchartsResize(chartRef){functionresize(){chartRef.valuechartRef.value.resize()}onMounted(()window.addEventListener(resize,resize))onUnmounted(()window.removeEventListener(resize,resize))}坑二canvas 内容缩放后模糊。transform: scale缩放的是位图canvas 画出来的东西放大后会发虚。解法是给 canvas 容器做反缩放补偿——外层 scale 放大了多少canvas 内部就按 DPR 多绘制多少倍。坑三缩放后点击事件坐标偏移。如果有拖拽、绘制等交互event.clientX是屏幕坐标需要除以缩放比才能换算成设计稿坐标。说白了scale 方案最适合设计稿钉死、不许乱的大屏项目但凡是有 canvas 图表、鼠标交互的地方都得额外做一层坐标换算。五、四种方案横向对比到底该选哪个维度rem flexiblevw/vhtransform: scale媒体查询响应式依赖 JS是改根字号否是算缩放比否适用场景移动端 H5移动端 H5 / 通用大屏 / 活动页官网 / 后台等比缩放宽度方向宽高独立整体等比断点切换1px 边框需单独处理需单独处理天然解决天然解决首屏闪烁可能JS 慢时无可能JS 慢时无第三方组件兼容需排除需排除无需处理无需处理学习成本低低中中我的选型建议普通移动端 H5直接上 vw postcss-px-to-viewport这是目前最省心的方案需要兼容老版本安卓 WebViewrem 方案更稳vw 在极低版本上有兼容问题数据大屏 / 固定设计稿活动页transform: scale一把梭内部全写 px企业官网 / 管理后台媒体查询 Flex/Grid 响应式布局不要强行等比缩放六、Vue3 项目中的工程化落地技巧1. 全局 1px 边框方案不管用 rem 还是 vw1px 边框都得单独处理。写一个全局样式类用伪元素 transform 缩放实现真正的物理 1px/* src/styles/border.css *//* 下边框 1px */.border-bottom-1px{position:relative;}.border-bottom-1px::after{content:;position:absolute;left:0;bottom:0;width:100%;height:1px;background:#e5e5e5;transform-origin:0 100%;}/* DPR 2 时缩到 0.5px */media(-webkit-min-device-pixel-ratio:2){.border-bottom-1px::after{transform:scaleY(0.5);}}/* DPR 3 时缩到 0.333px */media(-webkit-min-device-pixel-ratio:3){.border-bottom-1px::after{transform:scaleY(0.333);}}2. 刘海屏安全区域适配iOS 刘海屏、灵动岛、底部 Home 条会遮挡内容用env()函数处理!-- index.html --metanameviewportcontentwidthdevice-width, initial-scale1.0, viewport-fitcover/* src/styles/global.css */.safe-area-top{padding-top:env(safe-area-inset-top);}.safe-area-bottom{padding-bottom:env(safe-area-inset-bottom);}/* 兼容 iOS 11 以下的 constant() */.safe-area-bottom{padding-bottom:constant(safe-area-inset-bottom);padding-bottom:env(safe-area-inset-bottom);}你想想viewport-fitcover是让页面延伸到刘海区域的开关不开这个env()拿到的值全是 0安全区域适配等于白做。3. 禁止 iOS 字体自动缩放iOS Safari 会在横屏时自动放大字体加一行全局样式关掉/* src/styles/global.css */html{-webkit-text-size-adjust:100%;text-size-adjust:100%;}4. 高清屏图片适配用srcset按 DPR 提供不同分辨率的图片避免高清屏上图片模糊imgsrc/images/logo1x.pngsrcset/images/logo2x.png 2x, /images/logo3x.png 3xaltLogo/或者用 CSS 的image-set().logo{background-image:image-set(url(/images/logo1x.png)1x,url(/images/logo2x.png)2x);}七、总结适配不是选一个方案就完事屏幕适配这件事没有银弹。真实项目里往往是vw 做主体缩放 1px 边框单独处理 安全区域全局样式 媒体查询补断点的组合拳。记住三个原则先搞清楚设计稿基准375 还是 7501920×1080 还是其他所有换算都围绕它来哪些 px 不该转比怎么转更重要——边框、行内样式、第三方组件都得排除上线前多拿几台真机测尤其是刘海屏、折叠屏、高 DPR 设备模拟器测不出来的问题太多了最后说一句适配的终极目标不是在所有屏幕上一模一样而是在所有屏幕上都好用、都不难看。该等比的等比该自适应的自适应别一根筋。精彩推荐Vue3全屏实战一键搞定数据大屏Vue3深色模式实现方案一键切换零白屏Vue3组件封装从混乱到高复用的实战指南Vue3国际化实战vue-i18n避坑指南前端AI编辑器实现一键润色与智能写作本篇博客文章唯一版权归属©波仔椿
返回列表