开发移动端最主要的就是适配各种手机
vw vh
是相对viewport 视口的单位,配合meta标签可以直接使用,无需计算
1vw=1/100视口宽度
1vh=1/100视口高度
当前屏幕视口是375像素,1vw就是3.75px
postCss 提供了 把Css 转换AST的能力,类似于Babel
,为此我们可以编写一个postCss插件用于将px转换为vw
前置知识
px 固定的单位不会随着屏幕大小的变化而变化
百分比
- 百分比是子元素占父元素的宽度,然后让子元素撑起父元素的高度
- 其中百分比只能勉强解决容器的适配(比如高度无法用百分比表示),做不到字体的适配
- 字体和高度适配也需配合下面的rem方案
flex
跟百分比一样只能解决容器的适配不能处理字体的适配
rem
- 在之前我们用的是rem 根据根节点HTML font-size 去做缩放
- rem r=root 1rem = html 假如html根节点font-size =16px 1rem = 16px
- 有个问题375屏幕下适合多少HTML font-size 并不清楚之前引入的是淘宝的flexible.js来计算的,这个方案就额外多了计算的开销
vh、vw
- vw、vh是基于视口的布局方案,所以这个meta元素的视口必须声明。(解决宽高自动适配)
-
为什么加meta标签 默认的视口可能是大于屏幕的尺寸会出现滚动条
-
vw vh
是相对viewport 视口的单位,配合meta标签可以直接使用,无需计算1vw=1/100视口宽度
1vh=1/100视口高度
当前屏幕视口是375像素,1vw就是3.75像素
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no“>
移动设备具有各种不同的屏幕尺寸和分辨率,例如智能手机和平板电脑。为了提供更好的用户体验,网页需要根据设备的屏幕宽度进行自适应布局。如果不设置width=device-width,移动设备会按照默认的视口宽度(通常是较宽的桌面屏幕)来渲染网页,导致网页内容在移动设备上显示不正常,可能出现内容被截断或需要水平滚动的情况
初始化项目
npm init vite@latest
cnpm i
cnpm i less less-loader
cnpm i @vueuse/core
postCss
https://cn.vitejs.dev/config/shared-options.html#css-postcss
发现vite已经内置了postCss
https://www.postcss.com.cn/
postCss 提供了 把Css 转换AST的,类似于Babel
,为此我们可以编写一个插件用于将px转换为vw
根目录新建一个plugins文件夹新建两个文件pxto-viewport.ts type.ts
然后在 tsconfig.node.json 的includes 配置 "plugins/**/*",
compilerOptions 配置 noImplicitAny:false
tsconfig.node.json配置
postcss-px-to-viewport.ts
// postcss的插件已经是vite内置了,所以不需要再单独安装了
import type { Options } from "./type.ts";
import type { Plugin } from "postcss";
const defaultOptions = {
viewPortWidth: 375, //视窗的宽度,对应的是我们设计稿的宽度一般是375,ui设计稿宽度是多少就是多少
mediaQuery: false,
unitToConvert: "px",
};
export const pxToViewport = (
options: Options = defaultOptions
): Plugin => {
const opt = Object.assign({}, defaultOptions, options);
return {
postcssPlugin: "postcss-px-to-viewport",
// AtRulede等钩子函数
//
//css节点都会经过这个钩子
Declaration(node) {
// console.log(node);
console.log(node.prop, node.value);
// console.log("opt.viewPortWidth", opt.viewPortWidth); // 375
const value = node.value;
//匹配到px 转换成vw
if (value.includes(opt.unitToConvert)) {
const num = parseFloat(value); //考虑到有小数的情况
const transformValue =
(num / opt.viewPortWidth) * 100;
node.value = `${transformValue.toFixed(2)}vw`; //转换之后的值
}
},
};
};
type.ts
export interface Options {
viewPortWidth?: number;
mediaQuery?: boolean;
unitToConvert?: string;
}
vite.config.ts配置
引入我们写好的插件
https://cn.vitejs.dev/config/shared-options.html#css-postcss参考配置
import { pxToViewport } from "./plugins/postcss-px-to-viewport";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
css: {
postcss: {
plugins: [pxToViewport()],
},
},
});
用户可自定义设计稿宽度 (可省略在插件内部已给了默认值)
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { pxToViewport } from "./plugins/postcss-px-to-viewport";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
css: {
postcss: {
plugins: [
pxToViewport({
unitToConvert: "px", // 要转化的单位
viewPortWidth: 750, // UI设计稿的宽度
}),
],
},
},
});
组件中使用
自适应效果图
未使用插件前明显感觉中间被挤压了
使用插件后
全局换肤案例
全局字体更改案例
完整示例代码
main.ts中注释掉初始style.css样式
// postcss的插件已经是vite内置了,所以不需要再单独安装了
import type { Options } from "./type.ts";
import type { Plugin } from "postcss";
const defaultOptions = {
viewPortWidth: 375, //视窗的宽度,对应的是我们设计稿的宽度一般是375,ui设计稿宽度是多少就是多少
mediaQuery: false,
unitToConvert: "px",
};
export const pxToViewport = (
options: Options = defaultOptions
): Plugin => {
const opt = Object.assign({}, defaultOptions, options);
return {
postcssPlugin: "postcss-px-to-viewport",
// AtRulede等钩子函数
//
//css节点都会经过这个钩子
Declaration(node) {
// console.log(node);
console.log(node.prop, node.value);
// console.log("opt.viewPortWidth", opt.viewPortWidth); // 375
const value = node.value;
//匹配到px 转换成vw
/*
// 无法处理 border: 1px solid red;
if (value.includes(opt.unitToConvert)) {
const num = parseFloat(value); //考虑到有小数的情况
const transformValue =
(num / opt.viewPortWidth) * 100;
node.value = `${transformValue.toFixed(2)}vw`; //转换之后的值
} */
// 能处理 border: 1px solid red;
if (value.includes(opt.unitToConvert)) {
const regexp = new RegExp(
`\\d+${opt.unitToConvert}{1}`,
"gi"
);
const nodeVal = value.replace(regexp, (match) => {
const num = parseFloat(match);
const transformValue = Number(
(num / opt.viewPortWidth) * 100
);
return transformValue.toFixed(2) + "vw";
});
node.value = nodeVal;
}
},
};
};
官方插件postcss-px-to-viewport
已经帮我们处理好了各种兼容性
npm install postcss-px-to-viewport -D
vite.config.ts配置
可能vant第三库会存在挤压得问题可设置成375
const designWidth = webpack.resourcePath.includes(path.join('node_modules', 'vant')) ? 375 : 750;
import { fileURLToPath, URL } from 'url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import postcsspxtoviewport from "postcss-px-to-viewport" //插件
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), vueJsx()],
css: {
postcss: {
plugins: [
postcsspxtoviewport({
unitToConvert: 'px', // 要转化的单位
viewportWidth: 750, // UI设计稿的宽度
unitPrecision: 6, // 转换后的精度,即小数点位数
propList: ['*'], // 指定转换的css属性的单位,*代表全部css属性的单位都进行转换
viewportUnit: 'vw', // 指定需要转换成的视窗单位,默认vw
fontViewportUnit: 'vw', // 指定字体需要转换成的视窗单位,默认vw
selectorBlackList: ['ignore-'], // 指定不转换为视窗单位的类名,
minPixelValue: 1, // 默认值1,小于或等于1px则不进行转换
mediaQuery: true, // 是否在媒体查询的css代码中也进行转换,默认false
replace: true, // 是否转换后直接更换属性值
landscape: false // 是否处理横屏情况
})
]
}
},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
postcss-px-to-viewport.d.ts声明文件
declare module 'postcss-px-to-viewport' {
type Options = {
unitToConvert: 'px' | 'rem' | 'cm' | 'em',
viewportWidth: number,
viewportHeight: number, // not now used; TODO: need for different units and math for different properties
unitPrecision: number,
viewportUnit: string,
fontViewportUnit: string, // vmin is more suitable.
selectorBlackList: string[],
propList: string[],
minPixelValue: number,
mediaQuery: boolean,
replace: boolean,
landscape: boolean,
landscapeUnit: string,
landscapeWidth: number
}
export default function(options: Partial<Options>):any
}
引入声明文件 tsconfig.app.json postcss-px-to-viewport.d.ts跟vite.ts同级
{
"extends": "@vue/tsconfig/tsconfig.web.json",
"include": ["env.d.ts", "src/**/*", "src/**/*.vue", "postcss-px-to-viewport.d.ts"],
"exclude": ["src/**/__tests__/*"],
"compilerOptions": {
"composite": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}