vue3 + ts + vite 移动端适配

本文章使用 Vant UI 框架,其他 UI 框架可自行切换

本文章最终推荐方案为:postcss-px-to-viewport-8-plugin + 自定义行内样式转换插件

像素单位:px、em、rem、vw/vh等的区别?

  1. px:是像素单位。它是代表显示器上每一个显示的像素点,根据用户屏幕显示器的分辨率决定
  2. em:为相对单位,相对于当前元素内文本的字体尺寸。如果当前元素没有指定字体尺寸,那么以浏览器默认的字体尺寸为准。例如,当前元素设置了字体尺寸为24px,那么2em就代表48px
  3. rem:为相对单位,相对于<HTML>元素文本的字体尺寸。如果<HTML>元素没有指定字体尺寸,那么以浏览器默认的字体尺寸为准

    例如,<HTML>元素设置了字体尺寸为24px,那么2rem就代表48px

  4. vwvh:相对单位,相对于当前视口(),又叫 Viewport 

    例如,10vw代表当前视口宽度的10%,20vh代表当前视口高度的20%

  5. %:相对单位,相对于父元素的相关尺寸

    例如,父元素设置了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";
// 自动导入vue中hook reactive ref等
import AutoImport from "unplugin-auto-import/vite";
// 自动导入ui 组件
import Components from "unplugin-vue-components/vite";
// Vant 官方基于 unplugin-vue-components 提供的自动导入样式的解析器
import { VantResolver } from "@vant/auto-import-resolver";
// 这个path 需要安装的 @types/node
import path from "path";
// postcss8 插件
import postcsspxtoviewport8plugin from "postcss-px-to-viewport-8-plugin";

// https://vitejs.dev/config/
export default defineConfig(({ command, mode }) => {
  // 根据当前工作目录中的 `模式` 加载 .env 文件
  // 设置第三个参数为 '' 来加载所有环境变量,而不管是否有 `VITE_` 前缀。
  // 设置第三个参数为 'VITE_' 表示加载'VITE_'开头的环境变量
  const env = loadEnv(mode, process.cwd(), "VITE_");
  console.log(env);
  const port: number = (env.VITE_APP_PORT as any) || 80;
  return {
    // 应用访问路径 例如使用前缀 /admin/
    base: env.VITE_APP_CONTEXT_PATH,
    plugins: [
      vue(),
      AutoImport({
        // 安装两行后你会发现在组件中不用再导入ref,reactive等
        imports: ["vue", "vue-router"],
        // 存放的位置
        dts: "src/auto-import.d.ts",
      }),
      Components({
        // 存放的位置: 引入组件的,包括自定义组件
        dts: "src/components.d.ts",
        resolvers: [VantResolver()],
      }),
    ],
    // 配置别名
    resolve: {
      // https://cn.vitejs.dev/config/shared-options.html#resolve-alias
      alias: {
        // "~": path.resolve(__dirname, "./"), // ~代替./
        "@": path.resolve("./src"), // @代替src
      },
    },
    // https://cn.vitejs.dev/config/server-options
    server: {
      // 将此设置为 0.0.0.0 或者 true 将监听所有地址,包括局域网和公网地址
      host: true,
      // 配置启动并端口号
      // 注意:如果端口已经被使用,Vite 会自动尝试下一个可用的端口,所以这可能不是开发服务器最终监听的实际端口
      port: Number(port),
      // 服务启动时是否自动打开浏览器
      open: true,
      // 代理配置
      proxy: {
        [env.VITE_APP_BASE_API]: {
          // 要代理的服务器地址
          target: env.VITE_PROXY_TARGET_URL,
          // 允许跨域,可以代理反向的地址
          changeOrigin: true,
          // 将代理前缀替换为空
          // 如:http://xxx.com/dev-api/login  替换成 -> http://xxx.com/login
          rewrite: (path) =>
            path.replace(new RegExp("^" + env.VITE_APP_BASE_API), ""),
        },
      },
    },
    css: {
      // 适配移动端
      postcss: {
        plugins: [
          postcsspxtoviewport8plugin({
            unitToConvert: "px", // 需要转换的单位,默认为 px
            viewportWidth: 375, // UI设计稿的视口宽度
            unitPrecision: 5, // 单位转换后保留的精度
            propList: ["*"], // 能转化为vw的属性列表
            viewportUnit: "vw", // 希望使用的视口单位
            fontViewportUnit: "vw", // 字体使用的视口单位
            selectorBlackList: [], // 需要忽略的CSS选择器,不会转为视口单位,使用原有的px等单位。
            minPixelValue: 1, // 设置最小的转换数值,如果为1的话,只有大于1的值会被转换
            mediaQuery: true, // 媒体查询里的单位是否需要转换单位
            replace: true, //  是否直接更换属性值,而不添加备用属性
            exclude: [/node_modules/], // 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
            include: [], // 如果设置了include,那将只有匹配到的文件才会被转换
            landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
            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, 内容为:

// 虽然postcss-px-to-viewport-8-plugin做适配,但是行内样式不能转换为vw,所以我们自定义个插件,将内样式px转成vw
interface IdefaultsProp {
  unitToConvert: string;
  viewportWidth: number;
  unitPrecision: number;
  viewportUnit: string;
  fontViewportUnit: string;
  minPixelValue: number;
}

// 默认参数
const defaultsProp: IdefaultsProp = {
  unitToConvert: "px", // 需要转换的单位,默认为"px"
  viewportWidth: 375, // 设计稿的视口宽度,如传入函数,函数的参数为当前处理的文件路径
  unitPrecision: 5, // 单位转换后保留的精度
  viewportUnit: "vw", // 希望使用的视口单位
  fontViewportUnit: "vw", // 字体使用的视口单位
  minPixelValue: 1, // 设置最小的转换数值,如果为 1 的话,只有大于 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 = /([\s\S]+)/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
  },
  // 这里加载自定义的插件ts
  "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";
// 自动导入vue中hook reactive ref等
import AutoImport from "unplugin-auto-import/vite";
// 自动导入ui 组件
import Components from "unplugin-vue-components/vite";
// Vant 官方基于 unplugin-vue-components 提供的自动导入样式的解析器
import { VantResolver } from "@vant/auto-import-resolver";
// 这个path 需要安装的 @types/node
import path from "path";
// postcss8 插件
import postcsspxtoviewport8plugin from "postcss-px-to-viewport-8-plugin";
// 虽然postcss-px-to-viewport-8-plugin做适配,但是行内样式不能转换为vw,所以我们自定义个插件,将内样式px转成vw
import vitePluginStyleVwLoader from "./src/plugin/vite-plugin-style-vw-loader";

// https://vitejs.dev/config/
export default defineConfig(({ command, mode }) => {
  // 根据当前工作目录中的 `模式` 加载 .env 文件
  // 设置第三个参数为 '' 来加载所有环境变量,而不管是否有 `VITE_` 前缀。
  // 设置第三个参数为 'VITE_' 表示加载'VITE_'开头的环境变量
  const env = loadEnv(mode, process.cwd(), "VITE_");
  console.log(env);
  const port: number = (env.VITE_APP_PORT as any) || 80;
  return {
    // 应用访问路径 例如使用前缀 /admin/
    base: env.VITE_APP_CONTEXT_PATH,
    plugins: [
      // 该插件需要放在vue()之前
      vitePluginStyleVwLoader({
        unitToConvert: "px",
        viewportWidth: 375,
        unitPrecision: 5,
        viewportUnit: "vw",
        fontViewportUnit: "vw",
        minPixelValue: 1,
      }),
      AutoImport({
        // 全局引入插件
        // 安装两行后你会发现在组件中不用再导入ref,reactive等
        imports: ["vue", "vue-router"],
        // 存放的位置
        dts: "src/auto-import.d.ts",
      }),
      // 全局注册组件
      Components({
        // 默认自动导入的目录是: dirs: ['src/components']
        dirs: ["src/components"],
        // 存放的位置: 引入组件的,包括自定义组件
        dts: "src/components.d.ts",
        resolvers: [VantResolver()],
      }),
      vue(),
    ],
    // 配置别名
    resolve: {
      // https://cn.vitejs.dev/config/shared-options.html#resolve-alias
      alias: {
        // "~": path.resolve(__dirname, "./"), // ~代替./
        "@": path.resolve("./src"), // @代替src
      },
    },
    // https://cn.vitejs.dev/config/server-options
    server: {
      // 将此设置为 0.0.0.0 或者 true 将监听所有地址,包括局域网和公网地址
      host: true,
      // 配置启动并端口号
      // 注意:如果端口已经被使用,Vite 会自动尝试下一个可用的端口,所以这可能不是开发服务器最终监听的实际端口
      port: Number(port),
      // 服务启动时是否自动打开浏览器
      open: true,
      // 代理配置
      proxy: {
        [env.VITE_APP_BASE_API]: {
          // 要代理的服务器地址
          target: env.VITE_PROXY_TARGET_URL,
          // 允许跨域,可以代理反向的地址
          changeOrigin: true,
          // 将代理前缀替换为空
          // 如:http://xxx.com/dev-api/login  替换成 -> http://xxx.com/login
          rewrite: (path) =>
            path.replace(new RegExp("^" + env.VITE_APP_BASE_API), ""),
        },
      },
    },
    css: {
      // 适配移动端
      postcss: {
        plugins: [
          postcsspxtoviewport8plugin({
            unitToConvert: "px",
            viewportWidth: 375,
            unitPrecision: 5, // 单位转换后保留的精度
            propList: ["*"], // 能转化为vw的属性列表
            viewportUnit: "vw", // 希望使用的视口单位
            fontViewportUnit: "vw", // 字体使用的视口单位
            selectorBlackList: [], // 需要忽略的CSS选择器,不会转为视口单位,使用原有的px等单位。
            minPixelValue: 1, // 设置最小的转换数值,如果为1的话,只有大于1的值会被转换
            mediaQuery: true, // 媒体查询里的单位是否需要转换单位
            replace: true, //  是否直接更换属性值,而不添加备用属性
            exclude: [], // 忽略某些文件夹下的文件或特定文件,例如 'node_modules' 下的文件
            include: [], // 如果设置了include,那将只有匹配到的文件才会被转换
            landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
            landscapeUnit: "vw", // 横屏时使用的单位
            landscapeWidth: 1024, // 横屏时使用的视口宽度
          }),
        ],
      },
    },
  };
});

这样自定义样式+行内样式 px都会转vw了

  • 20
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
### 回答1: Vue3是一种前端开发框架,它的目标是提供更好的性能和开发体验。与之前的版本相比,Vue3在许多方面进行了改进和优化。 TS代表TypeScript,它是一种类型安全的编程语言。Vue3与TS的结合可以提供更强大的开发工具和更好的代码提示,使开发者能够更轻松地编写可维护的代码。 Vite是一种用于前端开发的构建工具。它基于ES模块来进行快速的热更新,从而提供了更快的开发体验。使用Vite可以在开发过程中实时预览和调试项目,大大提高了开发效率。 Vant是一套基于Vue移动端组件库。它提供了丰富的UI组件和样式,可以帮助开发者快速搭建漂亮且高效的移动端应用程序。 综上所述,Vue3 TS Vite Vant 是一种在前端开发中频繁使用的组合。它们共同提供了更好的性能、开发体验和移动端开发支持。开发者可以通过使用Vue3和TS来编写可维护的代码,并使用Vite和Vant来提高开发效率和用户体验。 ### 回答2: Vue3是基于TypeScript的一款前端框架,它相较于Vue2有着更加强大的性能和更高效的开发体验。 Vue3采用了模块化的设计,将代码拆分为不同的模块,使得开发者可以按需引入,减少了打包体积,提高了加载速度。此外,Vue3还引入了Composition API,这是一套全新的 API 设计风格,使得组件之间的业务逻辑可以更好地共享和封装,提高了代码的可复用性。 而Vite是一款由Vue团队开发的新一代前端构建工具,它具有快速的冷启动时间和热更新能力。Vite利用浏览器原生的 ES 模块导入能力,在开发过程中使用一种需要少量转换的现代化方式加载代码。这使得构建过程更加快速和高效,提高了开发效率。 而Vant是一款基于Vue3的移动端组件库,它提供了一系列常用的移动端UI组件,可以帮助开发者快速搭建漂亮的移动端界面。Vant的设计风格简洁大方,组件丰富多样,支持按需引入,可以满足各种不同的业务需求。 通过将Vue3、TypeScriptVite和Vant结合使用,开发者可以在项目中获得更好的开发体验和更高的效率。Vue3提供了强大的开发能力,TypeScript增强了代码的可读性和可维护性,Vite提供了快速的开发和构建速度,Vant则提供了丰富的移动端组件。这一组合使得前端开发更加高效、灵活和便捷。 ### 回答3: Vue3是一种用于构建用户界面的现代JavaScript框架,它结合了React的组件化开发和Angular的响应式数据绑定。相对于Vue2,Vue3提供了更快的性能、更小的包体积以及更好的开发体验。 TS是指TypeScript,它是JavaScript的一个超集,为JavaScript添加了静态类型检查和更强大的开发工具。使用TypeScript可以提供更好的代码提示、错误检查和重构支持,能够使代码更加健壮和易于维护。 ViteVue3的一个新型构建工具,它基于ES模块的原生导入机制,能够快速地在开发过程中进行热重载和快速构建。相比于Webpack等传统构建工具,Vite具有更快的冷启动速度和更低的内存占用,提供了更好的开发体验。 Vant是一套基于Vue3的移动组件库,它提供了丰富的UI组件和常用功能,能够快速构建出美观、高效的移动应用。Vant的组件能够自动适配不同屏幕尺寸,支持响应式布局和主题定制,开发者可以根据自己的需求进行扩展和定制。 综上所述,Vue3、TypeScriptVite和Vant是一套完整的技术栈,能够提供高效、可靠和易于维护的前端开发体验。在使用这些技术的过程中,开发者可以快速构建出优秀的Web应用和移动应用,并且能够享受到更好的性能和开发效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值