Skyroc Web Kit

@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 平铺到最外层,而是分成两层:

字段职责示例
applicationSkyroc Admin 预设语义,用来描述后台应用的差异plugins.projectInfo: falseproxy.enabledbuildTimeDefineNamecss.additionalData
viteVite 原生 UserConfig,作为最终覆盖层optimizeDepsserver.fsresolve.conditions、第三方 Vite 配置

这样设计是为了避免字段语义冲突。比如 pluginsbuildserverresolve 这些名字在 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.0open = trueport = 9527
proxy开发服务下根据环境变量创建代理

环境变量

环境变量只负责应用运行和代理相关配置,不再负责图标插件前缀。图标前缀通过插件 options 配置。

变量名作用
VITE_BASE_URL应用 base url
VITE_HTTP_PROXYY 时,在 dev server 中启用代理
VITE_PROXY_LOGY 时,打印代理请求日志
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.com

application 配置

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当前配置加载时生成的构建时间
configEnvVite 传入的 ConfigEnv
envloadEnv() 读取到的环境变量
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 包含 reactantdreact-routeril8n

追加 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
  }
});

插件配置

默认插件顺序:

  1. prependPlugins
  2. TanStack DevTools
  3. TanStack Router
  4. React
  5. Babel
  6. UnoCSS icon preset
  7. unplugin-icons and svg sprite
  8. auto-import
  9. html build meta
  10. vite-plugin-inspect
  11. remove-console
  12. project info
  13. 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/preset
  • reactCompilerPreset()

调整配置:

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忽略 componentsmodulesloadingerrornot-found
routeTokenlayout
targetreact
autoCodeSplittingtrue

覆盖路由目录:

export default defineConfig({
  application: {
    plugins: {
      router: {
        routesDirectory: './src/routes'
      }
    }
  }
});

图标

三个插件共享同一套图标解析规则:

  • unocss: 生成 icon class
  • unpluginIcon: 生成 icon React component 与本地 svg sprite
  • autoImport: 自动导入 icon component

默认图标配置:

选项默认值
iconPrefixicon
localIconPrefixicon-local
collectionNamelocalIconPrefix 推导,默认 local
localIconPathsrc/assets/svg-icon
scale1

覆盖图标目录和前缀:

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

默认自动导入:

  • react
  • FC type
  • react-i18next
  • ahooks
  • src/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/* 迁移到本包时,推荐顺序:

  1. 先保留应用现有行为,抽出可复用 helper 到 packages/web/admin-vite/src
  2. apps/admin/vite.config.ts 改为直接 import { defineConfig } from '@skyroc/web-admin-vite'
  3. 将 app 侧只保留差异配置,例如 application.css.additionalData
  4. 删除 app 本地 build/configbuild/pluginsbuild/shared 这类桥接代码
  5. 在 app 的 predevprebuildprepreview 中先构建 @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

On this page