vue3 + VIte + TS 移动端 H5 项目屏幕适配,PC端响应式布局
首先,感谢满子哥@小满zs的某站vue3视频教程,跟着学习到了很多东西啊。
学习之后,我们也来手搓一个插件,来支持手机端和PC端的响应式布局。
由于满子哥的教程只涉及到了H5端的适配,而且细节方面没有怎么处理,所以在这里我们会尽量完善,可能还有不足,还请大佬们指正哈哈。
OK,废话不多说,我们直接开始!
思路
- 通过 postcss 的
Declaration() API
来获取style标签中的属性名称和属性值。- 把属性值中包含
px
的单位全部转换成vw
。- 行内样式和另外一些不得不用 px 作为单位的数值我们特殊处理。
插件
- 我们首先创建一个 pxtovw.ts 的文件来编写插件。
pxtovw.ts
// vite 内置了 PostCss,无需手动安装
import type { Plugin } from "postcss";
export const options = {
basesize: 1, // 控制转换比例 默认 1
viewportWidth: 375, // 设计稿宽度,默认 375
};
interface Options {
basesize: number;
viewportWidth: number;
}
export const pxtovw = (option: Options = options): Plugin => {
return {
postcssPlugin: "px-to-vw",
// 这是 postcss 提供的一个 API,用以获取所有的 css 节点
// node.prop 属性名 node.value 属性值
Declaration(node) {
if (node.value.includes("px")) {
node.value = numInit(node.value, option);
}
},
};
};
// 把字符串中的 px 全部转化为 vw,其他的不动
// 实现方式有很多,你也可以定义属于你自己的方法
export function numInit(str: string, option: Options): string {
if (str.includes("px")) {
const opt = Object.assign({}, option);
let vwVal = "",
number = "0123456789p",
pxVal = "";
for (let v of str) {
if (number.includes(v)) pxVal += v;
else if (pxVal.length > 1 && v == "x") {
let base = Number(pxVal.slice(0, -1)) * opt.basesize;
vwVal += ((base / opt.viewportWidth) * 100).toFixed(2) + "vw";
pxVal = "";
} else {
if (pxVal.length) {
vwVal += pxVal;
pxVal = "";
}
vwVal += v;
}
}
return vwVal + pxVal;
} else return str;
}
// 特殊处理行内样式和另外一些不得不用 px 作为单位的数值
export function rem(num: number): number {
let clientWidth =
window.innerWidth ||
document.documentElement.clientWidth ||
document.body.clientWidth;
if (!clientWidth) return 0;
let fontSize = clientWidth / options.viewportWidth;
return num * fontSize * options.basesize;
}
- vite.config.ts 中注册插件。
vite.config.ts
import { pxtovw } from "./src/plugins/pxtovw";
export default defineConfig({
css: {
postcss: {
plugins: [pxtovw()],
},
},
});
可能你引入文件会爆红,我们按照提示去引用一下就好。
tsconfig.node.json
{
"extends": "@tsconfig/node20/tsconfig.json",
"include": [
"vite.config.*",
"vitest.config.*",
"cypress.config.*",
"nightwatch.conf.*",
"playwright.config.*",
"src/plugins/*", // 在这里添加路径,让 ts 识别到该路径下的文件
],
"compilerOptions": {
"composite": true,
"noEmit": true,
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"module": "ESNext",
"moduleResolution": "Bundler",
"types": ["node"]
}
}
应用
- H5 手机端应用适配,修改设计稿宽度,一般是 375。
export const options = {
basesize: 1, // 控制转换比例 默认 1
viewportWidth: 375, // 设计稿宽度,默认 375
};
- PC Web端应用适配,修改设计稿宽度,一般是 1920。
export const options = {
basesize: 1, // 控制转换比例 默认 1
viewportWidth: 1920, // 设计稿宽度,默认 1920
};
- 行内样式和另外一些不得不用 px 作为单位的数值。(例如:echarts 图表、el-table 的 max-height 配置)
这个方法有个缺陷,窗口大小变换后,需要手动刷新一下才能变为正常大小。
所以说这个适配方案更适合手机端。
<script setup lang="ts">
import { rem } from "@/src/plugins/pxtovw";
const box = ref()
onMounted(() => {
box.value.style.width = `${rem(100)}px`;
box.value.style.background = "pink";
})
</script>
<template>
<div ref="box" :style="{ height: `${rem(100)}px`, fontSize: `${rem(14)}px` }">大即正义</div>
</template>