1.环境变量&配置加载
import { defineConfig, loadEnv, ConfigEnv } from 'vite'
const env = loadEnv(mode.mode, process.cwd())
for (const k in env) {
process.env[k] = env[k]
}
process(node.js运行时环境的内置全局对象, 包含关于进程的信息和操作信息的方法,**主要用来访问环境变量,获取进程状态,控制进程生命周期**)
process.env 环境变量对象,包含当前进程的环境变量(如 NODE_ENV)
process.argv 启动Node进程时的命令行参数数组
process.env | 环境变量对象,包含当前进程的环境变量(如 NODE_ENV ) |
---|
process.argv | 启动 Node 进程时的命令行参数数组 |
---|
process.exit(code) | 结束进程,code 是退出码 |
---|
process.nextTick(fn) | 在当前事件循环尾部执行回调 |
---|
process.memoryUsage() | 获取进程的内存使用情况 |
---|
process.platform | 当前操作系统平台,如 win32 , linux , darwin |
---|
process.version | Node.js 版本号 |
---|
process.stdin 、process.stdout 、process.stderr |
---|
2.别名配置
const alias = {
'@': path.resolve(__dirname, './src'),
vue$: 'vue/dist/vue.runtime.esm-bundler.js',
}
3.剔除console & debugger
const esbuild = {
drop: NODE_ENV === 'development' ? [] : ['console', 'debugger'],
}
4.Rullup打包配置-代码分割
manualChunks(id) {
if (id.includes('node_modules')) {
if (id.split('node_modules/')[1].split('/')[0].includes('pnpm')) {
return id.split('node_modules/')[1].split('/')[1]
}
return id.split('node_modules/')[1].split('/')[0]
}
}
- 通过路径拆分,把每个 `node_modules` 包单独拆分成一个 chunk 文件。
- 对 pnpm 包做特殊拆分,拆成具体包名的 chunk。
- 目的是实现第三方库按包拆分,优化缓存利用和加载性能
5.开发服务器配置:
server: {
port: VITE_CLI_PORT,
overlay: {
warnings: false,
errors: true,
},
proxy: {
[VITE_BASE_API]: { target: VITE_BASE_PATH, changeOrigin: true, rewrite: ... },
'/mapapi': { target: VITE_MAP_PATH, changeOrigin: true, rewrite: ... },
'/sysSettingMessages': { target: VITE_WS_BASE_PATH, changeOrigin: true, secure: true, ws: true },
},
},
- 指定本地启动端口。
- 前端请求接口时,自动代理转发到对应的后端服务器,方便开发跨域。
- 支持 websocket 代理
6.构建相关配置:
build: {
minify: 'esbuild',
manifest: false,
sourcemap: VITE_ENV !== 'production' ? 'inline' : false,
outDir: 'dist',
reportCompressedSize: true,
rollupOptions,
chunkSizeWarningLimit: 800,
},
- 使用 esbuild 进行快速压缩(相比 terser 更快)。
- 生产环境关闭 sourcemap,开发环境开启 inline sourcemap 方便调试。
- 自定义打包输出目录和文件命名规则。
- 允许打包大小警告阈值设为 800kb。
- 启用 gzip 体积报告,辅助评估构建产物大小
7.自动导入&组件按需加载
AutoImport({
imports: ['vue', 'vue-router'],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
- `unplugin-auto-import` 实现 API(如 `ref`, `reactive`, `computed`, `onMounted`)的自动引入,避免反复写导入语句。
- `unplugin-vue-components` 实现自定义组件和 ElementPlus 组件的自动按需导入,避免全量引入,减小体积。
8.兼容老旧浏览器
legacyPlugin({
targets: ['Chrome >= 70', 'Safari >= 10.1', 'Firefox >= 54', 'Edge >= 15'],
}),
通过 Babel 插件转译和 polyfill,支持较老版本浏览器,保障兼容性。
9.外部依赖使用CDN
externalGlobals({
vue: 'Vue',
echarts: 'echarts',
'vue-demi': 'VueDemi',
tinymce: 'tinymce',
'@tinymce/tinymce-vue': 'Editor',
}),
生产构建时不打包指定的依赖库,改为使用 CDN 全局变量,减少包体积,利用 CDN 高速分发。
10.HTML模板注入&压缩
createHtmlPlugin({
minify: true,
entry: '/src/main.js',
template: '/index.html',
inject: { data: publicCDN },
}),
- 支持通过 ejs 语法向 `index.html` 模板注入变量,动态插入 CDN 地址或其他动态信息。
- 启用 HTML 压缩,减少体积。
11.骨架屏插件
skeletonPlugin(),
自定义骨架屏插件,改善页面加载体验,减少白屏
12.资源压缩:
viteCompression({
verbose: true,
disable: false,
threshold: 10240,
algorithm: 'gzip',
ext: '.gz',
}),
产出 gzip 压缩文件,提升资源传输效率,减少首屏加载时间。
13.打包分析
visualizer({
template: 'treemap',
filename: './node_modules/.cache/visualizer/stats.html',
open: true,
gzipSize: true,
brotliSize: true,
}),
打包后生成可视化分析报告,帮助开发者洞察依赖结构、包大小,辅助优化。
14.开发工具&全量引入
if (NODE_ENV === 'development') {
config.plugins.push(VueDevTools());
config.plugins.push(fullImportPlugin());
}
- 开发模式下集成 Vue Devtools 支持。
- 通过 `fullImportPlugin` 实现开发时全量引入依赖,避免模块按需加载导致的构建重复,提升开发体验。
完整配置代码(web端)
import { defineConfig, loadEnv, ConfigEnv } from 'vite'
import legacyPlugin from '@vitejs/plugin-legacy'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import Banner from 'vite-plugin-banner'
import * as path from 'path'
import * as dotenv from 'dotenv'
import * as fs from 'fs'
import vuePlugin from '@vitejs/plugin-vue'
import vueSetupExtend from 'vite-plugin-vue-setup-extend'
import GvaPosition from './vitePlugin/gvaPosition'
import fullImportPlugin from './vitePlugin/fullImport/fullImport.js'
import skeletonPlugin from './vitePlugin/skeletonPlugin/index.js'
import vueJsx from '@vitejs/plugin-vue-jsx'
import { createHtmlPlugin } from 'vite-plugin-html'
import viteCompression from 'vite-plugin-compression'
import visualizer from 'rollup-plugin-visualizer'
import externalGlobals from 'rollup-plugin-external-globals'
import VueDevTools from 'vite-plugin-vue-devtools'
import { publicCDN } from './public/publicCDN'
const viteConfig = defineConfig((mode) => {
console.log(`output->process`, process.argv)
const env = loadEnv(mode.mode, process.cwd())
console.log('👏vite环境变量', env)
const isFeatureEnabled = process.argv.includes('--d')
console.log(`output->isFeatureEnabled`, isFeatureEnabled)
for (const k in env) {
process.env[k] = env[k]
}
process.env['VITE_NOPERMISSION'] = isFeatureEnabled
const {
NODE_ENV,
VITE_CLI_PORT,
VITE_SERVER_PORT,
VITE_BASE_API,
VITE_BASE_PATH,
VITE_WS_BASE_PATH,
VITE_MAP_PATH,
VITE_NOPERMISSION,
VITE_ENV,
} = process.env
const timestamp = Date.parse(new Date())
const optimizeDeps = {
include: ['element-plus/es/components/**/*'],
}
const alias = {
'@': path.resolve(__dirname, './src'),
vue$: 'vue/dist/vue.runtime.esm-bundler.js',
}
const esbuild = {
drop: NODE_ENV === 'development' ? [] : [('console', 'debugger')],
}
const rollupOptions = {
output: {
chunkFileNames: 'static/js/[name]-[hash].js',
entryFileNames: 'static/js/[name]-[hash].js',
assetFileNames: 'static/[ext]/[name]-[hash].[ext]',
manualChunks(id) {
if (id.includes('node_modules')) {
if (id.toString().split('node_modules/')[1].split('/')[0].includes('pnpm')) {
return id.toString().split('node_modules/')[1].split('/')[1].toString()
}
return id.toString().split('node_modules/')[1].split('/')[0].toString()
}
},
compact: true,
},
plugins: [],
}
const config = {
mode: NODE_ENV,
base: NODE_ENV === 'development' ? './' : '/',
resolve: {
alias,
},
publicPath: '/',
server: {
port: VITE_CLI_PORT,
overlay: {
warnings: false,
errors: true,
},
proxy: {
[VITE_BASE_API]: {
target: `${VITE_BASE_PATH}`,
changeOrigin: true,
rewrite: (path) => {
if (VITE_BASE_PATH.includes('http:')) {
return path.replace(new RegExp('^' + '/api'), '')
}
return path
},
},
'/mapapi': {
target: `${VITE_MAP_PATH}`,
changeOrigin: true,
rewrite: (path) => path.replace(new RegExp('^' + '/mapapi'), ''),
},
'/sysSettingMessages': {
target: `${VITE_WS_BASE_PATH}`,
changeOrigin: true,
secure: true,
ws: true,
},
},
},
build: {
minify: 'esbuild',
manifest: false,
sourcemap: VITE_ENV != 'production' ? 'inline' : false,
outDir: 'dist',
reportCompressedSize: true,
rollupOptions,
chunkSizeWarningLimit: 800,
},
esbuild,
optimizeDeps,
external: ['vue', 'vue-demi', 'echarts', 'tinymce', '@tinymce/tinymce-vue'],
plugins: [
vuePlugin(),
vueJsx(),
AutoImport({
imports: [
'vue',
'vue-router',
],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
vueSetupExtend(),
legacyPlugin({
targets: ['Chrome >= 70', 'Safari >= 10.1', 'Firefox >= 54', 'Edge >= 15'],
}),
{
...externalGlobals({
vue: 'Vue',
echarts: 'echarts',
'vue-demi': 'VueDemi',
tinymce: 'tinymce',
'@tinymce/tinymce-vue': 'Editor',
}),
enforce: 'post',
apply: 'build',
},
createHtmlPlugin({
minify: true,
entry: '/src/main.js',
template: '/index.html',
inject: {
data: {
notoSansSc: publicCDN.notoSansSc,
vuescript: publicCDN.vuescript,
demiScript: publicCDN.demiScript,
echartsScript: publicCDN.echartsScript,
tinymceScript: publicCDN.tinymceScript,
tinymceVueScript: publicCDN.tinymceVueScript,
},
},
}),
skeletonPlugin(),
viteCompression({
verbose: true,
disable: false,
threshold: 10240,
algorithm: 'gzip',
ext: '.gz',
}),
visualizer({
template: 'treemap',
filename: './node_modules/.cache/visualizer/stats.html',
open: true,
gzipSize: true,
brotliSize: true,
}),
],
css: {
preprocessorOptions: {
scss: {
additionalData: `@use "@/style/element/index.scss" as *;`,
},
},
devSourcemap: true,
},
clearScreen: NODE_ENV === 'development' ? true : false,
}
if (NODE_ENV === 'development') {
config.plugins.push(VueDevTools())
config.plugins.push(fullImportPlugin())
} else {
}
return config
})
export default viteConfig