vue移动端适配(自适应)

11,011 阅读4分钟

问题

在做移动端开发时直接使用px会引发设备适配问题

其中涉及到的原因和概念有很多:像素、分辨率、PPIDPIDPDIPDPR、视口等等,在这里不多做解释(确实有点复杂不太好解释,需要后续组织组织语言)

问题演示:

views/About.vue

 <template>
   <div class="about">
     <div class="box"></div>
   </div>
 </template>
 ​
 <style scoped>
 .box {
   width: 150px;
   height: 75px;
   background-color: pink;
 }
 </style>

此时在宽度375px的设备上看,色块宽度是150px150 / 375 = 2 / 5,所以色块占设备宽度的2/5

image-20220609103601542.png

如果此时切换宽度为414px的设备,色块所占的比例将发生改变

image-20220609103505231.png

可以切换成ipad查看,宽度差距越大效果越明显

image-20220609104419905.png

解决方案一:rem

rem:相对单位,1rem = 根元素1 font-size大小

如果根元素 < html style="font-size: 20px;" >1rem = 20px2rem = 40px

如果根元素 < html style="font-size: 30px;" >1rem = 30px2rem = 60px

可以给 < html > 设置font-size值为设备宽度的1/10

设备宽为375px< html >font-size37.5px1rem = 37.5px

设备宽为414px< html >font-size41.4px1rem = 41.4px

设备宽为820px< html >font-size82px1rem = 82px

后续只需要使用rem为单位即可

main.js

 import Vue from 'vue'
 import App from './App.vue'
 import router from './router'
 import store from './store'
 ​
 // 获取屏幕分辨率的宽 / 10
 document.documentElement.style.fontSize = window.screen.width / 10 + 'px'
 ​
 Vue.config.productionTip = false
 ​
 new Vue({
   router,
   store,
   render: h => h(App)
 }).$mount('#app')

views/About.vue

 <template>
   <div class="about">
     <div class="box"></div>
   </div>
 </template>
 ​
 <style scoped>
 .box {
   width: 4rem;
   height: 2rem;
   background-color: pink;
 }
 </style>

设备宽为375px< html >font-size37.5px1rem = 37.5px

4 * 37.5 = 150,=> 150 / 375, => 2 / 5,色块占设备宽度的2/5

image-20220609125238180.png

设备宽为414px< html >font-size41.4px1rem = 41.4px

4 * 41..4 = 165.6,=> 165.6 / 414, => 2 / 5,色块占设备宽度的2/5

image-20220609125751470.png

设备宽为820px< html >font-size82px1rem = 82px

4 * 82 = 328,=> 328 / 820, => 2 / 5,色块占设备宽度的2/5

image-20220609130044726.png

解决方案二:rem插件

amfe-flexible

amfe-flexible可以自动给 < html > 设置font-size值为设备宽度的1/10

npm:www.npmjs.com/package/amf…

安装:

 npm i amfe-flexible

配置:

main.js

 import Vue from 'vue'
 import App from './App.vue'
 import router from './router'
 import store from './store'
 ​
 // 引入amfe-flexible
 import 'amfe-flexible'
 ​
 Vue.config.productionTip = false
 ​
 new Vue({
   router,
   store,
   render: h => h(App)
 }).$mount('#app')
 ​

views/About.vue

 <template>
   <div class="about">
     <div class="box"></div>
   </div>
 </template>
 ​
 <style scoped>
 .box {
   width: 4rem;
   height: 2rem;
   ackground-color: pink;
 }
 </style>

效果同方案一rem一样,就不截图了,当然兼容性会更好

postcss-pxtorem

rem方案虽然可以解决适配问题,但是如果UI设计稿使用的是px单位,自己转换成rem比较麻烦

所以需要使用postcss-pxtorem,可以把px转换为rem

npm:www.npmjs.com/package/pos…

安装:

 npm i postcss-pxtorem

postcss-pxtoremamfe-flexible需要配合使用

配置:

无需在main.js中设置,需要在项目根目录下新增配置文件postcss.config.js

propList:表示哪些属性的px转换为rem'*' 表示所有属性都转换

rootValue:转换参考值,写UI设计稿宽度的1/10,假设UI设计稿宽度是750px所以写75,如UI设计稿是375px则写37.5

 module.exports = {
     plugins: {
         'postcss-pxtorem': {
             rootValue() {
                 return 75;
             },
             propList: ['*'],
         },
     },
 };

示例:

假设UI设计稿宽度是750px,色块设计稿标注是150px,此时色块在设计稿占比是150 / 750 = 1 / 5

在开发时可以直接写设计稿标注的大小

转换规则:

150 / 配制的参考值,配制的参考值是75,也就是150 / 75,得到结果为2,就是2rem

views/About.vue

 <template>
   <div class="about">
     <div class="box"></div>
   </div>
 </template>
 ​
 <style scoped>
 .box {
   /* 150 / 75 = 2 => 2rem */
   width: 150px;
   height: 150px;
   background-color: pink;
 }
 </style>

设备宽为375pxamfe-flexible会设置1rem = 37.5px,所以2rem实际大小就是75px

设备实际显示75 / 375 = 1 / 5,色块占设备宽度的1/5,与UI设计稿色块占比保持一致

image-20220609150338840.png

设备宽为414pxamfe-flexible会设置1rem = 41.4px,所以2rem实际大小就是82.8px

设备实际显示82.8 / 41.4 = 1 / 5,色块占设备宽度的1/5,与UI设计稿色块占比保持一致

image-20220609150519870.png

设备宽为820pxamfe-flexible会设置1rem = 82px,所以2rem实际大小就是164px

设备实际显示164 / 820 = 1 / 5,色块占设备宽度的1/5,与UI设计稿色块占比保持一致

image-20220609150626047.png

vant兼容

如果使用了vant组件库,vant的设计稿是根据375px去设计的,为了避免冲突需要单独设置下

postcss.config.js

 module.exports = {
     plugins: {
         'postcss-pxtorem': {
             rootValue({ file }) {
                 return file.indexOf('vant') !== -1 ? 37.5 : 75;
             },
             propList: ['*'],
         },
     },
 };

解决方案三:vw、vh

vw:视窗宽度,1vw=视窗宽度的1%

vh:视窗高度,1vh=视窗高度的1%

vwvh是相对单位,实际大小会随着设备宽度的变化自动变化

示例:

views/About.vue

 <template>
   <div class="about">
     <div class="box"></div>
   </div>
 </template>
 ​
 <style scoped>
 .box {
   /* 40vw就是设备宽度的40% */
   width: 40vw;
   height: 20vw;
   background-color: pink;
 }
 </style>

切换不同宽度设备可以发现,色块所占比例不变

375px

image-20220609113842405.png

414px

image-20220609114006180.png

820px

image-20220609114101830.png

解决方案四:viewport插件

postcss-px-to-viewport

postcss-px-to-viewport:将px转化为vw/vh

npm:www.npmjs.com/package/pos…

安装:

 npm i postcss-px-to-viewport

配置:

postcss.config.js

viewportWidth:设计稿的视口宽度

 module.exports = {
     plugins: {
         'postcss-px-to-viewport': {
             viewportWidth: 750,
         },
     },
 };

示例:

假设UI设计稿宽度是750px,色块设计稿标注是300px,此时色块在设计稿占比是300 / 750 = 2 / 5

在开发时可以直接写设计稿标注的大小

views/About.vue

 <template>
   <div class="about">
     <div class="box"></div>
   </div>
 </template>
 ​
 <style scoped>
 .box {
   /* 300 / 750 = 2 / 5 => 40vw */
   width: 300px;
   height: 150px;
   background-color: pink;
 }
 </style>

转换规则:

300 / viewportWidth,配制的参考值是750300 / 750得到结果为2/5,设备实际显示色块宽度占比就是2/5也就是40vw,与UI设计稿占比一致

效果同方案三vw一样,就不截图了

vant兼容

如果使用了vant组件库,vant的设计稿是根据375px去设计的,为了避免冲突需要单独设置下

 const path = require('path');
 module.exports = ({ file }) => {
     const designWidth = file.includes(path.join('node_modules', 'vant')) ? 375 : 750;
     return {
         plugins: {
             "postcss-px-to-viewport": {
                 viewportWidth: designWidth,
             }
         }
     }
 }