【vite配置】vue3+vite项目vite.config.ts的配置详解(学习笔记)

5,378 阅读8分钟

1.vite.config.ts文件

/// <reference types="vitest" />
/*留在最后*/

import { defineConfig, loadEnv, ConfigEnv, UserConfig } from "vite";
import { resolve } from "path";
// 自定义 封装器 打包工具
import { wrapperEnv } from "./build/getEnv";
// 自定义 代理打包规则 js
import { createProxy } from "./build/proxy";
// plugins 也可以接受将多个插件作为单个元素的预设。这对于使用多个插件实现的复杂特性(如框架集成)很有用。该数组将在内部被扁平化(flatten)。
import { createVitePlugins } from "./build/plugins";

import pkg from "./package.json";
import dayjs from "dayjs";

const { dependencies, devDependencies, name, version } = pkg;

// package.json 的配置文件信息dependencies
// 最后一次打包时间
const __APP_INFO__ = {
  pkg: { dependencies, devDependencies, name, version },
  lastBuildTime: dayjs().format("YYYY-MM-DD HH:mm:ss")
};

/** 配置项文档:https://cn.vitejs.dev/config */
export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => {
  // console.log(command, mode); //command:开发环境为 serve   生产环境为:build   mode:指向对应的生产环境  dev或prod
  // root 和 envDir 选项会影响加载行为
  const root = process.cwd();
  // loadEnv 函数来加载指定的 .env 文件。
  // 在配置中指明将会把 serve 和 build 时的模式 都 覆盖掉。
  // mode  是指.env.后缀指向的生产环境
  const env = loadEnv(mode, root);
  // 读取所有环境变量配置文件到process.env
  const viteEnv = wrapperEnv(env);

  return {
    /** 打包时根据实际情况修改 base 开发或生产环境服务的公共基础路径 */
    base: viteEnv.VITE_PUBLIC_PATH,
    // 项目根目录(index.html 文件所在的位置)
    root,
    resolve: {
      // 当使用文件系统路径的别名时,请始终使用绝对路径。相对路径的别名值会原封不动地被使
      // 路径别名   @ 替代 指向 src文件
      alias: {
        "@": resolve(__dirname, "./src"),
        "vue-i18n": "vue-i18n/dist/vue-i18n.cjs.js"
      }
      // extensions: [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json", ".vue"],//导入时想要省略的扩展名列表。官方不建议省略。vue影响ide类型支持
    },
    define: {
      // 包含 配置文件 和 最后一次打包时间
      // 定义全局常量替换方式。其中每项在开发环境下会被定义在全局,而在构建时被静态替换。
      __APP_INFO__: JSON.stringify(__APP_INFO__)
    },
    css: {
      // 指定传递给 CSS 预处理器的选项。
      preprocessorOptions: {
        // sass预处理
        scss: {
          // 所有预处理器选项还支持 additionalData 选项,可以用于为每个样式内容注入额外代码。
          additionalData: `@import "@/styles/var.scss";`
        }
      }
    },
    // 服务器选项
    server: {
      /** 是否开启 HTTPS */
      // https: false,
      // 指定服务器应该监听哪个 IP 地址。 如果将此设置为 0.0.0.0 或者 true 将监听所有地址,包括局域网和公网地址。
      host: "0.0.0.0",
      // 指定开发服务器端口。  如我的 :9090
      port: viteEnv.VITE_PORT,
      // 开发服务器启动时,自动在浏览器中打开应用程序。
      open: viteEnv.VITE_OPEN,
      // 为开发服务器配置 CORS。
      cors: true,
      /** 端口被占用时,是否直接退出 */
      // strictPort: false,
      // Load proxy configuration from .env.development   为开发服务器配置自定义代理规则。
      proxy: createProxy(viteEnv.VITE_PROXY)
    },
    // 需要用到的插件数组。 扁平化插件数组  env配置文件
    /*   {                                                                                                                                                                                                                            09:08:42
      VITE_GLOB_APP_TITLE: 'xxx',
      VITE_PORT: 9090,
      VITE_OPEN: false,
      VITE_REPORT: false,
      VITE_USER_NODE_ENV: 'development',
      VITE_PUBLIC_PATH: '/',
      VITE_ROUTER_MODE: 'hash',
      VITE_DROP_CONSOLE: true,
      VITE_PWA: false,
      VITE_API_URL: '/api',
      VITE_PROXY: [
        [
          '/api',
          'https://xxxxxxxxxx/xxx/xxxx'
        ]
      ]
    }
    plugins 也可以接受将多个插件作为单个元素的预设。这对于使用多个插件实现的复杂特性(如框架集成)很有用。该数组将在内部被扁平化(flatten)。 
    */
    //  总结 就是 vite 插件
    plugins: createVitePlugins(viteEnv),
    esbuild: {
      // 默认情况下,esbuild 会被应用在 ts、jsx、tsx 文件。
      /* # 打包时是否删除 console  debugger
           VITE_DROP_CONSOLE = true */
      pure: viteEnv.VITE_DROP_CONSOLE ? ["console.log", "debugger"] : []
    },
    build: {
      // 指定输出路径(相对于 项目根目录).
      outDir: "dist",
      /** Vite 2.6.x 以上需要配置 minify: "terser", terserOptions 才能生效
       * 设置为 false 可以禁用最小化混淆,或是用来指定使用哪种混淆器。
       * 当设置为 'terser' 时必须先安装 Terser。
       * 默认值esbuild
       *   esbuild 打包更快,但是不能去除 console.log,terser打包慢,但能去除 console.log
           minify: "terser",
           terserOptions: {
      	     compress: {
      		     drop_console: viteEnv.VITE_DROP_CONSOLE,
      		     drop_debugger: true
      	     }
              format: {
                   // 删除注释
                   // comments: false
                   // }
      },
       */
      minify: "esbuild",
      // 构建后是否生成 source map 文件。
      sourcemap: true,
      // 禁用 gzip 压缩大小报告,可略微减少打包时间
      reportCompressedSize: false,
      // 规定触发警告的 chunk 大小 消除打包大小超过 500kb 警告
      chunkSizeWarningLimit: 2000,
      // 自定义底层的 Rollup 打包配置。
      /* 
      该选项用于匹配需要排除在 bundle 外部的模块,它的值可以是一个接收模块 id 参数并返回 true 
      (表示外部依赖)或 false (表示非外部依赖)的函数,也可以是一个模块 ID 数组或者正则表达式。 */
      rollupOptions: {
        output: {
          // Static resource classification and packaging 静态资源分类和包装
          chunkFileNames: "assets/js/[name]-[hash].js",
          // 该选项用于指定 chunks 的入口文件模式,其值也可以是一个函数,对每个入口 chunk 调用以返回匹配模式。
          entryFileNames: "assets/js/[name]-[hash].js",
          // 该选项的值是一个匹配模式,用于自定义构建结果中的静态资源名称,
          assetFileNames: "assets/[ext]/[name]-[hash].[ext]"
        }
      }
      /** 打包后静态资源目录 */
      //  assetsDir: "static"
    },

    /* vitest 将读取你的项目根目录的 vite.config.ts 文件以匹配插件并设置为你的 Vite 应用程序。如果想使用不同的配置进行测试,你可以:
       创建 vitest.config.ts,优先级更高。
       将 --config 选项传递给 CLI,例如 vitest --config ./path/to/vitest.config.ts 。
       在 defineConfig 中使用 process.env.VITEST 或 mode 属性(默认值是 test)在 vite.config.ts 中有条件的应用不同的配置。
       要配置 vitest 本身,请在你的 Vite 配置中添加 test 属性。如果你使用 vite 的 defineConfig 你还需要将 三斜线指令 写在配置文件的顶部。 */
    test: {
      // 匹配包含测试文件的 glob 规则。默认值: ['**/*.{test,spec}.?(c|m)[jt]s?(x)']
      include: ["tests/**/*.test.ts"],
      // Vitest 中的默认测试环境是一个 Node.js 环境。如果你正在构建 Web 端应用程序,你可以使用 jsdom 或 happy-dom 这种类似浏览器(browser-like)
      // 的环境来替代 Node.js。 如果你正在构建边缘计算函数,你可以使用 edge-runtime 环境
      environment: "jsdom"
    }
  };
});

2. 工具函数

1).getEnv.ts

import path from "path";

export function isDevFn(mode: string): boolean {
  return mode === "development";
}

export function isProdFn(mode: string): boolean {
  return mode === "production";
}

export function isTestFn(mode: string): boolean {
  return mode === "test";
}

/**
 * Whether to generate package preview
 * 是否生成包预览
 */
export function isReportMode(): boolean {
  return process.env.VITE_REPORT === "true";
}

// Read all environment variable configuration files to process.env 读取所有环境变量配置文件到process.env
export function wrapperEnv(envConf: Recordable): ViteEnv {
  const ret: any = {};

  for (const envName of Object.keys(envConf)) {
    let realName = envConf[envName].replace(/\\n/g, "\n");
    realName = realName === "true" ? true : realName === "false" ? false : realName;
    // 端口号
    if (envName === "VITE_PORT") realName = Number(realName);
    // 代理
    if (envName === "VITE_PROXY") {
      try {
        realName = JSON.parse(realName);
      } catch (error) {}
    }
    ret[envName] = realName;
  }
  return ret;
}

// ret数据   拆分键值对 将所有env内容结构成可用数据
/* {                                                                                                                                                                                                                 10:38:25
  VITE_GLOB_APP_TITLE: 'xxxxx',
  VITE_PORT: 9090,
  VITE_OPEN: false,
  VITE_REPORT: true,
  VITE_USER_NODE_ENV: 'development',
  VITE_PUBLIC_PATH: '/',
  VITE_ROUTER_MODE: 'hash',
  VITE_DROP_CONSOLE: true,
  VITE_PWA: false,
  VITE_API_URL: '/api',
  VITE_PROXY: [
    [
      '/api',
      'https://xxxx/xxxxx'
    ]
  ]
}
 */

/**
 * Get user root directory
 * @param dir file path
 */
export function getRootPath(...dir: string[]) {
  return path.resolve(process.cwd(), ...dir);
}

2).proxy.ts

import type { ProxyOptions } from "vite";

type ProxyItem = [string, string];

type ProxyList = ProxyItem[];

type ProxyTargetList = Record<string, ProxyOptions>;

/**
 * 创建代理,用于解析 .env.development 代理配置
 * @param list
 * 
 *  传入数据:[
    [
      '/api',                //prefix
      'https://xxxx/xxxxx'   //target
    ]
  ]
 */

export function createProxy(list: ProxyList = []) {
  const ret: ProxyTargetList = {};
  for (const [prefix, target] of list) {
    const httpsRE = /^https:\/\//;
    const isHttps = httpsRE.test(target); // 匹配是否是https 还是 http 正确的服务器地址
    // https://github.com/http-party/node-http-proxy#options
    ret[prefix] = {
      target: target, // 需要代理的域名
      changeOrigin: true /** 是否允许跨域 */,
      ws: true, // 是否启用websockets
      rewrite: path => path.replace(new RegExp(`^${prefix}`), ""), //重写匹配的字段,如果不需要在请求路径上,重写为""
      ...(isHttps ? { secure: false } : {}) // https is require secure=false  是不是https 不检查安全问题。设置后,可以接受运行在HTTPS上,可以使用无效证书的后端服务器。
    };
  }
  return ret;
}

3).plugins.ts

import { resolve } from "path";
import { PluginOption } from "vite";
import { VitePWA } from "vite-plugin-pwa";
import { createHtmlPlugin } from "vite-plugin-html";
import { visualizer } from "rollup-plugin-visualizer";
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
import UnoCSS from "unocss/vite";
import vue from "@vitejs/plugin-vue";
import svgLoader from "vite-svg-loader";
import vueJsx from "@vitejs/plugin-vue-jsx";
import eslintPlugin from "vite-plugin-eslint";
import viteCompression from "vite-plugin-compression";
import vueSetupExtend from "unplugin-vue-setup-extend-plus/vite";

/**
 * 创建 vite 插件
 * @param viteEnv
 */
export const createVitePlugins = (viteEnv: ViteEnv): (PluginOption | PluginOption[])[] => {
  const { VITE_GLOB_APP_TITLE, VITE_REPORT, VITE_PWA } = viteEnv;
  return [
    vue(),
    // vue 可以使用 jsx/tsx 语法
    vueJsx(),
    // esLint 报错信息显示在浏览器界面上
    eslintPlugin(),
    // name 可以写在 script 标签上
    vueSetupExtend({}),
    // 创建打包压缩配置
    /** 将 SVG 静态图转化为 Vue 组件 */
    svgLoader({ defaultImport: "url" }),
    createCompression(viteEnv),
    // 注入变量到 html 文件
    /** UnoCSS */
    UnoCSS(),
    // Vite处理html模板
    createHtmlPlugin({
      minify: true,
      viteNext: true,
      inject: {
        data: { title: VITE_GLOB_APP_TITLE }
      }
    }),
    // 使用 svg 图标
    createSvgIconsPlugin({
      iconDirs: [resolve(process.cwd(), "src/assets/icons")],
      symbolId: "icon-[dir]-[name]"
    }),
    /** 自动按需引入 (已更改为完整引入,所以注释了) */
    // AutoImport({
    //   dts: "./types/auto-imports.d.ts",
    //   /** 自动按需导入 Element Plus 相关函数,比如 ElMessage */
    //   resolvers: [ElementPlusResolver()],
    //   /** 根据自动按需导入的相关 API,生成 .eslintrc-auto-import.json 文件供 Eslint 识别 */
    //   eslintrc: {
    //     enabled: true, // 默认 false
    //     filepath: "./types/.eslintrc-auto-import.json", // 默认 "./.eslintrc-auto-import.json"
    //     globalsPropValue: true // 默认 true (true | false | "readonly" | "readable" | "writable" | "writeable")
    //   }
    // }),
    // Components({
    //   dts: "./types/components.d.ts",
    //   /** 自动按需导入 Element Plus 组件 */
    //   resolvers: [ElementPlusResolver()]
    // })

    // vitePWA
    VITE_PWA && createVitePwa(viteEnv),
    // 是否生成包预览,分析依赖包大小做优化处理
    VITE_REPORT && (visualizer({ filename: "stats.html", gzipSize: true, brotliSize: true }) as PluginOption)
  ];
};

/**
 * @description 根据 compress 配置,生成不同的压缩规则
 * @param viteEnv
 */
const createCompression = (viteEnv: ViteEnv): PluginOption | PluginOption[] => {
  const { VITE_BUILD_COMPRESS = "none", VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE } = viteEnv;
  const compressList = VITE_BUILD_COMPRESS.split(",");
  const plugins: PluginOption[] = [];
  if (compressList.includes("gzip")) {
    plugins.push(
      viteCompression({
        ext: ".gz",
        algorithm: "gzip",
        deleteOriginFile: VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE
      })
    );
  }
  if (compressList.includes("brotli")) {
    plugins.push(
      viteCompression({
        ext: ".br",
        algorithm: "brotliCompress",
        deleteOriginFile: VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE
      })
    );
  }
  return plugins;
};

/**
 * @description VitePwa
 * @param viteEnv
 */
const createVitePwa = (viteEnv: ViteEnv): PluginOption | PluginOption[] => {
  const { VITE_GLOB_APP_TITLE } = viteEnv;
  return VitePWA({
    registerType: "autoUpdate",
    manifest: {
      name: VITE_GLOB_APP_TITLE,
      short_name: VITE_GLOB_APP_TITLE,
      theme_color: "#ffffff",
      icons: [
        {
          src: "/logo.png",
          sizes: "192x192",
          type: "image/png"
        },
        {
          src: "/logo.png",
          sizes: "512x512",
          type: "image/png"
        },
        {
          src: "/logo.png",
          sizes: "512x512",
          type: "image/png",
          purpose: "any maskable"
        }
      ]
    }
  });
};

3. env环境变量配置实例

1). env

# title
VITE_GLOB_APP_TITLE = xxx xxx

# 本地运行端口号
VITE_PORT = 9090

# 启动时自动打开浏览器
VITE_OPEN = false

# 打包后是否生成包分析文件
VITE_REPORT = true

2). .env.development

# 本地环境
VITE_USER_NODE_ENV = development

# 公共基础路径
VITE_PUBLIC_PATH = /

# 路由模式
# Optional: hash | history
VITE_ROUTER_MODE = hash

# 打包时是否删除 console
VITE_DROP_CONSOLE = true

# 是否开启 VitePWA
VITE_PWA = false

# 开发环境接口地址
VITE_API_URL = /api

# 开发环境跨域代理,支持配置多个
VITE_PROXY = [["/api","https://xxx.xxx.com/mock/629d727e6163854a32e8307e"]]
# VITE_PROXY = [["/api","https://xxx.xxx.xx/mock/f81e8333c1a9276214bcdbc170d9e0a0"]]
# VITE_PROXY = [["/api-easymock","https://xxx.xxx.com"],["/api-fastmock","https://www.xxx.xx"]]