vue3 + ts + vite 移动端适配
本文章使用 Vant UI 框架,其他 UI 框架可自行切换
本文章最终推荐方案为:postcss-px-to-viewport-8-plugin + 自定义行内样式转换插件
像素单位:px、em、rem、vw/vh等的区别?
px:是像素单位。它是代表显示器上每一个显示的像素点,根据用户屏幕显示器的分辨率决定em:为相对单位,相对于当前元素内文本的字体尺寸。如果当前元素没有指定字体尺寸,那么以浏览器默认的字体尺寸为准。例如,当前元素设置了字体尺寸为24px,那么2em就代表48pxrem:为相对单位,相对于<HTML>元素文本的字体尺寸。如果<HTML>元素没有指定字体尺寸,那么以浏览器默认的字体尺寸为准
例如,<HTML>元素设置了字体尺寸为24px,那么2rem就代表48px
vw和vh:相对单位,相对于当前视口(),又叫 Viewport
例如,10vw代表当前视口宽度的10%,20vh代表当前视口高度的20%
%:相对单位,相对于父元素的相关尺寸
例如,父元素设置了height: 100px,那么它的子元素height: 50%就代表50px
总结:一般移动端适配,使用 rem布局 或 Viewport (vw/vh)
rem 布局
如果需要使用 rem 单位进行适配,常常使用postcss-pxtorem 或 lib-flexible 两个方案
postcss-pxtorem 是一款 PostCSS 插件,用于将 px 单位转化为 rem 单位
postcss-pxtorem 已有4年没有更新,因此它是不支持 PostCSS 8.0+ 版本的(PostCss 8.0+ 以上的版本是主流,也是未来的方向),因此不推荐使用该方案
lib-flexible 用于设置 rem 基准值
访问 lib-flexible 的github仓库,文档中有这么一段话
由于viewport单位得到众多浏览器的兼容,lib-flexible这个过渡方案已经可以放弃使用,不管是现在的版本还是以前的版本,都存有一定的问题。建议大家开始使用viewport来替代此方。
因此不推荐使用该方案
Viewport布局
Vant 默认使用 px 作为样式单位(很多UI框架都是默认px),如果需要使用 viewport 单位 (vw, vh, vmin, vmax),推荐使用 postcss-px-to-viewport 进行转换。
postcss-px-to-viewport 是一款 PostCSS 插件,用于将 px 单位转化为 vw/vh 单位。
postcss-px-to-viewport 不适配postcss 8.0+ 最新版本的, 已弃用,目前有基于postcss 8 8.0+的插件:postcss-px-to-viewport-8-plugin
安装插件
npm install postcss postcss-loader postcss-px-to-viewport-8-plugin -D
配置 vite.config.ts
import { defineConfig, loadEnv } from "vite";
import vue from "@vitejs/plugin-vue";
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import { VantResolver } from "@vant/auto-import-resolver";
import path from "path";
import postcsspxtoviewport8plugin from "postcss-px-to-viewport-8-plugin";
export default defineConfig(({ command, mode }) => {
const env = loadEnv(mode, process.cwd(), "VITE_");
console.log(env);
const port: number = (env.VITE_APP_PORT as any) || 80;
return {
base: env.VITE_APP_CONTEXT_PATH,
plugins: [
vue(),
AutoImport({
imports: ["vue", "vue-router"],
dts: "src/auto-import.d.ts",
}),
Components({
dts: "src/components.d.ts",
resolvers: [VantResolver()],
}),
],
resolve: {
alias: {
"@": path.resolve("./src"),
},
},
server: {
host: true,
port: Number(port),
open: true,
proxy: {
[env.VITE_APP_BASE_API]: {
target: env.VITE_PROXY_TARGET_URL,
changeOrigin: true,
rewrite: (path) =>
path.replace(new RegExp("^" + env.VITE_APP_BASE_API), ""),
},
},
},
css: {
postcss: {
plugins: [
postcsspxtoviewport8plugin({
unitToConvert: "px",
viewportWidth: 375,
unitPrecision: 5,
propList: ["*"],
viewportUnit: "vw",
fontViewportUnit: "vw",
selectorBlackList: [],
minPixelValue: 1,
mediaQuery: true,
replace: true,
exclude: [/node_modules/],
include: [],
landscape: false,
landscapeUnit: "vw",
landscapeWidth: 1024,
}),
],
},
},
};
案例:
<template>
<!-- 适配生效 -->
<div class="div1">div1</div>
<!-- 适配不生效:写在style里面的则不能转换,插件不支持这种方式 -->
<div style="width: 185px; height: 50px; background-color: blue">div2</div>
</template>
<script setup lang="ts"></script>
<style>
.div1 {
width: 185px;
height: 50px;
background-color: red;
}
</style>
虽然postcss-px-to-viewport-8-plugin做适配,但是行内样式不能转换为vw,所以我们自定义个插件,将内样式px转成vw
创建文件 src/plugin/vite-plugin-style-vw-loader.ts, 内容为:
interface IdefaultsProp {
unitToConvert: string;
viewportWidth: number;
unitPrecision: number;
viewportUnit: string;
fontViewportUnit: string;
minPixelValue: number;
}
const defaultsProp: IdefaultsProp = {
unitToConvert: "px",
viewportWidth: 375,
unitPrecision: 5,
viewportUnit: "vw",
fontViewportUnit: "vw",
minPixelValue: 1,
};
function toFixed(number: number, precision: number) {
const multiplier = Math.pow(10, precision + 1),
wholeNumber = Math.floor(number * multiplier);
return (Math.round(wholeNumber / 10) * 10) / multiplier;
}
function createPxReplace(
viewportSize: number,
minPixelValue: number,
unitPrecision: number,
viewportUnit: any
) {
return function ($0: any, $1: any) {
if (!$1) return;
const pixels = parseFloat($1);
if (pixels <= minPixelValue) return;
return toFixed((pixels / viewportSize) * 100, unitPrecision) + viewportUnit;
};
}
const templateReg: RegExp = /([sS]+)/gi;
const pxGlobalReg: RegExp = /(d+)px/gi;
function vitePluginStyleVWLoader(customOptions: IdefaultsProp = defaultsProp) {
return {
name: "vite-plugin-style-vw-loader",
transform(code: any, id: any) {
customOptions = Object.assign(defaultsProp, customOptions);
if (/.vue$/.test(id)) {
let _source = "";
if (templateReg.test(code)) {
_source = code.match(templateReg)[0];
}
if (pxGlobalReg.test(_source)) {
const $_source = _source.replace(
pxGlobalReg,
createPxReplace(
customOptions.viewportWidth,
customOptions.minPixelValue,
customOptions.unitPrecision,
customOptions.viewportUnit
)
);
code = code.replace(_source, $_source);
}
}
return { code };
},
};
}
export default vitePluginStyleVWLoader;
修改tsconfig.node.json 配置为:
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts", "src/plugin/vite-plugin-style-vw-loader.ts"]
}
修改 vite.config.ts 配置为如下:
import { defineConfig, loadEnv } from "vite";
import vue from "@vitejs/plugin-vue";
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import { VantResolver } from "@vant/auto-import-resolver";
import path from "path";
import postcsspxtoviewport8plugin from "postcss-px-to-viewport-8-plugin";
import vitePluginStyleVwLoader from "./src/plugin/vite-plugin-style-vw-loader";
export default defineConfig(({ command, mode }) => {
const env = loadEnv(mode, process.cwd(), "VITE_");
console.log(env);
const port: number = (env.VITE_APP_PORT as any) || 80;
return {
base: env.VITE_APP_CONTEXT_PATH,
plugins: [
vitePluginStyleVwLoader({
unitToConvert: "px",
viewportWidth: 375,
unitPrecision: 5,
viewportUnit: "vw",
fontViewportUnit: "vw",
minPixelValue: 1,
}),
AutoImport({
imports: ["vue", "vue-router"],
dts: "src/auto-import.d.ts",
}),
Components({
dirs: ["src/components"],
dts: "src/components.d.ts",
resolvers: [VantResolver()],
}),
vue(),
],
resolve: {
alias: {
"@": path.resolve("./src"),
},
},
server: {
host: true,
port: Number(port),
open: true,
proxy: {
[env.VITE_APP_BASE_API]: {
target: env.VITE_PROXY_TARGET_URL,
changeOrigin: true,
rewrite: (path) =>
path.replace(new RegExp("^" + env.VITE_APP_BASE_API), ""),
},
},
},
css: {
postcss: {
plugins: [
postcsspxtoviewport8plugin({
unitToConvert: "px",
viewportWidth: 375,
unitPrecision: 5,
propList: ["*"],
viewportUnit: "vw",
fontViewportUnit: "vw",
selectorBlackList: [],
minPixelValue: 1,
mediaQuery: true,
replace: true,
exclude: [],
include: [],
landscape: false,
landscapeUnit: "vw",
landscapeWidth: 1024,
}),
],
},
},
};
});
这样自定义样式+行内样式 px都会转vw了
相关知识
vue3
移动端
手机移动端快速开发
如何从零高效的开发一款适配 Android 和 iOS 的移动端App
Html+Css+js实现春节倒计时效果(移动端和PC端)
移动Web实训DAY
一分钟告诉你建行移动端网络学习怎么学
简约排版鲜花植物类移动端店铺首页
基于深度卷积神经网络的移动端花卉识别系统
电商平台移动端应用开发及维护服务合同.doc
网址: vue3 + ts + vite 移动端适配 https://www.huajiangbk.com/newsview948688.html
上一篇: [移动端适配]从一个祖传项目谈移 |
下一篇: 手机淘宝——flexible.j |
推荐分享

- 1君子兰什么品种最名贵 十大名 4012
- 2世界上最名贵的10种兰花图片 3364
- 3花圈挽联怎么写? 3286
- 4迷信说家里不能放假花 家里摆 1878
- 5香山红叶什么时候红 1493
- 6花的意思,花的解释,花的拼音 1210
- 7教师节送什么花最合适 1167
- 8勿忘我花图片 1103
- 9橄榄枝的象征意义 1093
- 10洛阳的市花 1039