@skyroc/web-admin-vite
Skyroc Admin 的 Vite 配置预设包 — 收口构建产物、开发代理、路由插件、React 插件、图标插件、自动导入、别名与开发服务器默认配置
概述
@skyroc/web-admin-vite 是 Skyroc Admin 应用的 Vite 预设层。它的目标不是替代 Vite,而是把管理后台项目中高度重复、且容易在不同应用之间漂移的配置沉淀成共享入口。
它主要负责:
- 统一
build输出规则,包括 CSS、图片、页面 chunk、组件 chunk 和基础 vendor chunk - 统一 dev server、preview server、React dedupe、路径别名和预热文件
- 统一开发代理规则,支持主服务与多后端服务代理
- 统一 TanStack Router、React、Babel、UnoCSS、unplugin-icons、auto-import、HTML meta、inspect、remove-console 和项目启动提示插件
- 提供
application级预设配置,同时保留vite字段作为原生 Vite 配置覆盖层
应用层应尽量保持 vite.config.ts 简洁,只描述本应用的差异;共享规则放在 @skyroc/web-admin-vite 内维护。
适用场景
当一个管理后台应用符合下面约定时,推荐直接使用该预设:
- 使用 React + Vite
- 使用 TanStack Router,页面目录为
src/pages - 使用
@指向src,~指向应用根目录 - 本地图标目录默认位于
src/assets/svg-icon - 使用 UnoCSS、unplugin-icons、unplugin-auto-import
- 通过
.env中的VITE_*变量配置 base url、代理开关和后端服务地址
如果应用只需要少量差异,优先通过 application 覆盖预设;只有必须使用 Vite 原生能力时,再使用 vite 字段。
架构
apps/admin/vite.config.ts
│
└─ import { defineConfig } from '@skyroc/web-admin-vite'
│
▼
packages/web/admin-vite/src/config.ts
├─ loadEnv() 读取应用环境变量
├─ createAdminApplicationConfig() 生成 admin preset Vite 配置
├─ setupAdminVitePlugins() 生成内置插件组
└─ mergeConfig(adminConfig, vite) 合并原始 Vite 覆盖配置
│
▼
Vite runtime包内职责划分:
src/
├── config.ts defineConfig 主入口,负责应用级配置编排
├── build.ts 构建产物命名、manual chunks、SCSS preprocessor
├── proxy.ts 服务配置解析与 Vite dev proxy 创建
├── time.ts BUILD_TIME 生成
├── types.ts 图标配置、插件插槽、公共配置类型
└── plugins/
├── index.ts 内置插件顺序与开关
├── router.ts TanStack Router 插件默认配置
├── react.ts @vitejs/plugin-react
├── babel.ts Jotai preset + React Compiler preset
├── unocss.ts UnoCSS icon preset
├── unplugin-icon.ts unplugin-icons + svg sprite
├── auto-import.ts React / i18n / ahooks / AntD / icon 自动导入
├── html.ts buildTime meta 注入
├── info.ts 终端项目提示
└── icon-utils.ts 图标前缀、集合名、本地图标路径统一解析快速开始
最常见的应用配置只需要声明 SCSS 全局注入:
import { defineConfig } from '@skyroc/web-admin-vite';
export default defineConfig({
application: {
css: {
additionalData: '@use "@/styles/scss/global.scss" as *;'
}
}
});如果完全接受默认预设,可以进一步简化:
import { defineConfig } from '@skyroc/web-admin-vite';
export default defineConfig();需要根据 Vite 命令或 mode 动态配置时,传入工厂函数:
import { defineConfig } from '@skyroc/web-admin-vite';
export default defineConfig(configEnv => {
return {
application: {
base: configEnv.mode === 'prod' ? '/admin/' : '/'
}
};
});配置入口
defineConfig 支持三种写法:
defineConfig();
defineConfig({ application: {}, vite: {} });
defineConfig(configEnv => ({ application: {}, vite: {} }));完整结构:
import { defineConfig } from '@skyroc/web-admin-vite';
export default defineConfig({
application: {
// Skyroc Admin preset options
},
vite: {
// 原始 Vite UserConfig,在 admin preset 之后 merge
}
});application 是推荐配置面,描述后台应用预设差异。vite 是逃生口,用于补充或覆盖 Vite 原生配置。
为什么分成 application 和 vite
这个包刻意没有把 Vite 原生 options 平铺到最外层,而是分成两层:
| 字段 | 职责 | 示例 |
|---|---|---|
application | Skyroc Admin 预设语义,用来描述后台应用的差异 | plugins.projectInfo: false、proxy.enabled、buildTimeDefineName、css.additionalData |
vite | Vite 原生 UserConfig,作为最终覆盖层 | optimizeDeps、server.fs、resolve.conditions、第三方 Vite 配置 |
这样设计是为了避免字段语义冲突。比如 plugins、build、server、resolve 这些名字在 Vite 里本来就存在,但本包也需要提供 admin 语义:
export default defineConfig({
application: {
plugins: {
router: false,
autoImport: {
antd: false
}
},
build: {
manualChunks: {
charts: ['echarts']
}
}
},
vite: {
server: {
fs: {
strict: false
}
}
}
});如果允许这样平铺:
export default defineConfig({
plugins: [],
build: {},
server: {}
});plugins 就无法清楚表达它到底是 Vite 的 PluginOption[],还是 admin preset 的插件开关对象;build 也会同时像 Vite 的 BuildOptions 和 admin 的构建预设配置。长期看,这会让 API 既不像 Vite,也不像 admin preset。
因此推荐规则是:
- 后台应用语义放
application - Vite 原生能力放
vite - 需要覆盖同一项时,
vite最后合并,优先级更高 - 如果要完全自己控制某块 preset,先用
application.xxx = false关闭,再在vite里补原生配置
默认预设
defineConfig 默认会生成这些配置:
| 配置项 | 默认行为 |
|---|---|
base | 优先使用 application.base,否则读取 VITE_BASE_URL,再否则为 / |
build | 使用 admin 构建产物命名和基础 manual chunks |
define | 注入 BUILD_TIME |
css | 按需注入 SCSS additionalData |
plugins | 启用 admin 内置插件组 |
preview | 默认端口 9725 |
resolve | 默认 @ -> src、~ -> .,并 dedupe React runtime |
server | 默认 host = 0.0.0.0、open = true、port = 9527 |
proxy | 开发服务下根据环境变量创建代理 |
环境变量
环境变量只负责应用运行和代理相关配置,不再负责图标插件前缀。图标前缀通过插件 options 配置。
| 变量名 | 作用 |
|---|---|
VITE_BASE_URL | 应用 base url |
VITE_HTTP_PROXY | 为 Y 时,在 dev server 中启用代理 |
VITE_PROXY_LOG | 为 Y 时,打印代理请求日志 |
VITE_SERVICE_BASE_URL | 默认后端服务地址 |
VITE_OTHER_SERVICE_BASE_URL | 其他后端服务地址,支持 JSON5 字符串 |
示例:
VITE_BASE_URL=/
VITE_HTTP_PROXY=Y
VITE_PROXY_LOG=Y
VITE_SERVICE_BASE_URL=https://api.example.com
VITE_OTHER_SERVICE_BASE_URL={ auth: "https://auth.example.com" }默认代理路径:
| 服务 | 代理前缀 |
|---|---|
| 主服务 | /proxy-default |
| 其他服务 | /proxy-${key} |
例如:
VITE_OTHER_SERVICE_BASE_URL={ auth: "https://auth.example.com" }会创建:
/proxy-auth -> https://auth.example.comapplication 配置
root 与 envDir
默认 root 是当前命令执行目录,envDir 默认跟随 root。
export default defineConfig({
application: {
root: process.cwd(),
envDir: process.cwd()
}
});一般应用不需要配置这两项。只有当 Vite 命令执行目录和应用目录不一致时才需要显式指定。
base
配置应用 base url。支持静态值,也支持根据上下文动态返回。
export default defineConfig({
application: {
base: '/admin/'
}
});export default defineConfig({
application: {
base: context => (context.isBuild ? '/admin/' : '/')
}
});配置函数可读取的上下文:
| 字段 | 说明 |
|---|---|
buildTime | 当前配置加载时生成的构建时间 |
configEnv | Vite 传入的 ConfigEnv |
env | loadEnv() 读取到的环境变量 |
isBuild | 当前是否是 vite build |
isPreview | 当前是否是 preview |
isServe | 当前是否是 dev server |
root | 当前应用根目录 |
css
配置 SCSS 全局注入:
export default defineConfig({
application: {
css: {
additionalData: '@use "@/styles/scss/global.scss" as *;'
}
}
});也可以根据环境动态返回:
export default defineConfig({
application: {
css: {
additionalData: context => {
return context.isBuild
? '@use "@/styles/scss/global.scss" as *;'
: '@use "@/styles/scss/dev.scss" as *;';
}
}
}
});关闭 CSS 预设:
export default defineConfig({
application: {
css: false
}
});build
默认构建规则:
- CSS 输出到
css/[name]-[hash].css - 图片输出到
images/[name]-[hash].[ext] - 页面 chunk 输出到
js/pages/**/[name]-[hash].js - 组件 chunk 输出到
js/components/[name]-[hash].js - 其他 JS 输出到
js/[name]-[hash].js - 默认 manual chunks 包含
react、antd、react-router、il8n
追加 manual chunks:
export default defineConfig({
application: {
build: {
manualChunks: {
charts: ['echarts'],
query: ['@tanstack/react-query']
}
}
}
});调整路径识别规则:
export default defineConfig({
application: {
build: {
componentsDirPattern: '/src/shared/components/',
pagesDirPattern: '/src/routes/'
}
}
});关闭 build 预设:
export default defineConfig({
application: {
build: false
}
});server
默认 dev server:
{
host: '0.0.0.0',
open: true,
port: 9527,
warmup: {
clientFiles: ['./index.html', './src/{pages,components}/*']
}
}覆盖配置:
export default defineConfig({
application: {
server: {
open: false,
port: 3000,
warmupClientFiles: ['./index.html', './src/pages/**/*']
}
}
});关闭 server 预设:
export default defineConfig({
application: {
server: false
}
});preview
默认 preview 端口为 9725:
export default defineConfig({
application: {
preview: {
port: 4173
}
}
});关闭 preview 预设:
export default defineConfig({
application: {
preview: false
}
});proxy
默认代理启用条件:
- 当前命令是
serve - 不是 preview
VITE_HTTP_PROXY=Y
手动控制:
export default defineConfig({
application: {
proxy: {
enabled: context => context.isServe && !context.isPreview,
enableLog: true
}
}
});自定义服务配置:
export default defineConfig({
application: {
proxy: {
serviceConfig: {
baseURL: 'https://api.example.com',
proxyPattern: '/api',
other: [
{
baseURL: 'https://auth.example.com',
proxyPattern: '/auth-api'
}
]
}
}
}
});关闭代理预设:
export default defineConfig({
application: {
proxy: false
}
});resolve
默认别名:
| 别名 | 指向 |
|---|---|
@ | src |
~ | 应用根目录 |
默认 dedupe:
['react', 'react-dom', 'react/jsx-dev-runtime', 'react/jsx-runtime']覆盖别名:
export default defineConfig({
application: {
resolve: {
rootAlias: false,
srcAlias: 'source'
}
}
});关闭 React dedupe:
export default defineConfig({
application: {
resolve: {
dedupeReact: false
}
}
});关闭 resolve 预设:
export default defineConfig({
application: {
resolve: false
}
});buildTime
默认会注入:
define: {
BUILD_TIME: JSON.stringify(buildTime)
}默认时间格式为 YYYY-MM-DD HH:mm:ss,默认时区为 Asia/Shanghai。
修改格式:
export default defineConfig({
application: {
buildTime: {
format: 'YYYY-MM-DD HH:mm',
timezone: 'Asia/Shanghai'
}
}
});修改注入变量名:
export default defineConfig({
application: {
buildTimeDefineName: '__BUILD_TIME__'
}
});关闭构建时间注入:
export default defineConfig({
application: {
buildTimeDefineName: false
}
});插件配置
默认插件顺序:
prependPlugins- TanStack DevTools
- TanStack Router
- React
- Babel
- UnoCSS icon preset
- unplugin-icons and svg sprite
- auto-import
- html build meta
- vite-plugin-inspect
- remove-console
- project info
appendPlugins
每个内置插件都支持两种配置方式:
plugins: {
inspect: false, // 关闭
projectInfo: { // 传 options
enabled: false
}
}自定义插件插槽
prependPlugins 插入在所有内置插件之前,appendPlugins 插入在所有内置插件之后。
import type { Plugin } from 'vite';
import { defineConfig } from '@skyroc/web-admin-vite';
const myPlugin: Plugin = {
name: 'my-plugin'
};
export default defineConfig({
application: {
plugins: {
prependPlugins: [myPlugin]
}
}
});React
传给 @vitejs/plugin-react:
export default defineConfig({
application: {
plugins: {
react: {
jsxRuntime: 'automatic'
}
}
}
});关闭 React 插件:
export default defineConfig({
application: {
plugins: {
react: false
}
}
});Babel
Babel 插件默认包含:
jotai-babel/presetreactCompilerPreset()
调整配置:
export default defineConfig({
application: {
plugins: {
babel: {
jotai: false,
reactCompiler: false
}
}
}
});关闭 Babel:
export default defineConfig({
application: {
plugins: {
babel: false
}
}
});Router
默认 TanStack Router 配置:
| 选项 | 默认值 |
|---|---|
routesDirectory | ./src/pages |
generatedRouteTree | ./src/features/router/routeTree.gen.ts |
routeFileIgnorePattern | 忽略 components、modules、loading、error、not-found |
routeToken | layout |
target | react |
autoCodeSplitting | true |
覆盖路由目录:
export default defineConfig({
application: {
plugins: {
router: {
routesDirectory: './src/routes'
}
}
}
});图标
三个插件共享同一套图标解析规则:
unocss: 生成 icon classunpluginIcon: 生成 icon React component 与本地 svg spriteautoImport: 自动导入 icon component
默认图标配置:
| 选项 | 默认值 |
|---|---|
iconPrefix | icon |
localIconPrefix | icon-local |
collectionName | 从 localIconPrefix 推导,默认 local |
localIconPath | src/assets/svg-icon |
scale | 1 |
覆盖图标目录和前缀:
import { fileURLToPath } from 'node:url';
import { defineConfig } from '@skyroc/web-admin-vite';
const localIconPath = fileURLToPath(new URL('./src/icons', import.meta.url));
export default defineConfig({
application: {
plugins: {
unocss: {
iconPrefix: 's-icon',
localIconPrefix: 's-icon-local',
localIconPath
},
unpluginIcon: {
iconPrefix: 's-icon',
localIconPrefix: 's-icon-local',
localIconPath
},
autoImport: {
iconPrefix: 's-icon',
localIconPrefix: 's-icon-local',
localIconPath
}
}
}
});如果只是普通 admin 应用,建议保持默认图标目录和前缀,避免三个插件重复配置。
Auto Import
默认自动导入:
reactFCtypereact-i18nextahookssrc/components/**src/config.ts- Ant Design 的
A前缀组件,例如AButton -> Button - 图标组件
追加扫描目录:
export default defineConfig({
application: {
plugins: {
autoImport: {
dirs: ['src/components/**', 'src/hooks/**'],
dts: 'src/types/auto-imports.d.ts'
}
}
}
});关闭 Ant Design A 前缀解析:
export default defineConfig({
application: {
plugins: {
autoImport: {
antd: false
}
}
}
});Html
默认 build 时向 index.html 的 <head> 注入构建时间:
<meta name="buildTime" content="2026-05-25 12:00:00">修改 meta 名称:
export default defineConfig({
application: {
plugins: {
html: {
metaName: 'x-build-time'
}
}
}
});Project Info
projectInfo 在 Vite 启动时向终端打印项目提示。
export default defineConfig({
application: {
plugins: {
projectInfo: {
message: 'Skyroc Admin',
colors: ['#646cff', 'magenta'],
boxenOptions: {
borderStyle: 'round',
padding: 0.5
}
}
}
}
});关闭:
export default defineConfig({
application: {
plugins: {
projectInfo: false
}
}
});Inspect 与 Remove Console
关闭 inspect:
export default defineConfig({
application: {
plugins: {
inspect: false
}
}
});配置 remove-console:
export default defineConfig({
application: {
plugins: {
removeConsole: {
includes: ['log', 'warn']
}
}
}
});合并 Vite 原生配置
需要使用 Vite 原生能力时,放到 vite 字段。
import { defineConfig } from '@skyroc/web-admin-vite';
export default defineConfig({
application: {
css: {
additionalData: '@use "@/styles/scss/global.scss" as *;'
}
},
vite: {
optimizeDeps: {
include: ['antd']
},
resolve: {
conditions: ['source']
}
}
});vite 会在 admin 预设之后合并,因此它适合做最终覆盖。应用需要覆盖同一配置项时,优先评估是否已有 application 配置项;没有再用 vite。
迁移建议
从应用本地 build/* 迁移到本包时,推荐顺序:
- 先保留应用现有行为,抽出可复用 helper 到
packages/web/admin-vite/src - 将
apps/admin/vite.config.ts改为直接import { defineConfig } from '@skyroc/web-admin-vite' - 将 app 侧只保留差异配置,例如
application.css.additionalData - 删除 app 本地
build/config、build/plugins、build/shared这类桥接代码 - 在 app 的
predev、prebuild、prepreview中先构建@skyroc/web-admin-vite
应用入口应尽量保持这种形态:
import { defineConfig } from '@skyroc/web-admin-vite';
export default defineConfig({
application: {
css: {
additionalData: '@use "@/styles/scss/global.scss" as *;'
}
}
});常见问题
图标前缀是不是还从 env 读取?
不是。图标前缀已经收口为插件 options:
plugins: {
unocss: { iconPrefix: 'icon', localIconPrefix: 'icon-local' },
unpluginIcon: { iconPrefix: 'icon', localIconPrefix: 'icon-local' },
autoImport: { iconPrefix: 'icon', localIconPrefix: 'icon-local' }
}默认值就是 icon / icon-local,普通应用无需配置。
为什么要 dedupe React?
共享 workspace 包在 Vite dev 模式下可能解析到不同 React 实例,触发 Invalid hook call。默认 dedupe 这些运行时入口:
['react', 'react-dom', 'react/jsx-dev-runtime', 'react/jsx-runtime']除非确定所有依赖解析稳定,否则不要关闭 dedupeReact。
为什么 workspace 消费前要先构建本包?
包的发布入口指向 dist/index.mjs。本地 workspace 直接消费时,Vite 运行时也会加载 dist。如果源码已变但 dist 未更新,应用会使用旧逻辑。
建议在应用脚本里加:
{
"scripts": {
"build:admin-vite": "pnpm --filter @skyroc/web-admin-vite build",
"predev": "pnpm run build:admin-vite",
"prebuild": "pnpm run build:admin-vite",
"prepreview": "pnpm run build:admin-vite"
}
}什么时候用 application,什么时候用 vite?
优先用 application,因为它表达的是 admin 应用语义,例如代理、构建、插件开关、别名、dev server。
只有这些情况再用 vite:
- 需要配置 Vite 原生能力,且本包没有提供对应语义字段
- 需要最终覆盖 admin preset 生成的配置
- 接入第三方 Vite 插件,且不适合放入
prependPlugins/appendPlugins
验证命令
修改本包后建议至少运行:
pnpm --filter @skyroc/web-admin-vite typecheck
pnpm --filter @skyroc/web-admin-vite build如果同时改了应用消费方式,再运行:
pnpm --filter skyroc-admin typecheck