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"]]