Umi 配置文件 .umirc.ts 详解
一、配置文件是什么?
1.1 配置文件的作用
.umirc.ts 是 Umi 项目的"大脑",控制着:
| 控制内容 | 说明 |
|---|---|
| 路由规则 | 页面路径如何映射 |
| 构建行为 | 如何编译、打包代码 |
| 插件功能 | 启用哪些功能插件 |
| 开发体验 | 代理、热更新等 |
| 部署配置 | 公共路径、输出目录等 |
1.2 配置文件的位置
Umi 支持两种配置方式(二选一):
项目根目录/
├── .umirc.ts ← 方式一:单文件配置(推荐)
│
├── config/ ← 方式二:配置目录(复杂项目)
│ ├── config.ts ← 主配置
│ ├── routes.ts ← 路由配置
│ ├── proxy.ts ← 代理配置
│ └── theme.ts ← 主题配置
选择建议:
- 配置简单(<100行):使用
.umirc.ts - 配置复杂(>100行):使用
config/目录拆分
二、创建第一个配置文件
2.1 创建文件
在项目根目录创建 .umirc.ts:
typescript
// .umirc.ts
import { defineConfig } from 'umi';
export default defineConfig({
// 配置内容写在这里
});
2.2 为什么要用 defineConfig?
typescript
// ❌ 不推荐:直接导出对象
export default {
title: 'My App',
};
// ✅ 推荐:使用 defineConfig
import { defineConfig } from 'umi';
export default defineConfig({
title: 'My App',
});
defineConfig 的作用:
| 功能 | 说明 |
|---|---|
| 类型提示 | 提供完整的 TypeScript 类型提示 |
| 配置校验 | 启动时检查配置是否正确 |
| 默认值合并 | 自动填充默认配置 |
2.3 最简配置示例
typescript
// .umirc.ts
import { defineConfig } from 'umi';
export default defineConfig({
title: 'Hello Umi',
});
启动后,页面标题会变成 "Hello Umi"。
三、配置加载流程详解
3.1 完整加载流程
┌─────────────────────────────────────────────────────────────┐
│ umi dev / umi build │
│ ↓ │
│ 1. 确定配置文件位置 │
│ ├── 优先读取 .umirc.ts │
│ └── 不存在则读取 config/config.ts │
│ ↓ │
│ 2. 解析 TypeScript │
│ ├── 使用 ts-node 或 babel 解析 │
│ └── 支持 import、async/await 语法 │
│ ↓ │
│ 3. 执行配置文件,获取导出值 │
│ export default defineConfig({ ... }) │
│ ↓ │
│ 4. 合并默认配置 │
│ 用户配置会覆盖默认配置 │
│ ↓ │
│ 5. 加载插件 │
│ ├── 内置插件 │
│ ├── presets(预设插件集) │
│ └── plugins(用户插件) │
│ ↓ │
│ 6. 执行插件的 modifyConfig 钩子 │
│ 插件可以修改最终配置 │
│ ↓ │
│ 7. 生成最终配置 │
│ 写入 .umi/core/config.ts │
│ ↓ │
│ 8. 根据配置生成临时文件 │
│ ├── 路由文件 .umi/core/route.tsx │
│ ├── 入口文件 .umi/umi.ts │
│ └── 插件运行时 .umi/plugin-xxx/ │
└─────────────────────────────────────────────────────────────┘
3.2 查看最终配置
bash
# 打印最终配置(调试用)
umi dev --print-config
输出示例:
javascript
{
title: 'Hello Umi',
history: { type: 'browser' },
publicPath: '/',
outputPath: 'dist',
// ... 更多配置
}
3.3 配置文件中可以使用的环境变量
typescript
// .umirc.ts
import { defineConfig } from 'umi';
// process.env 在配置文件中可用
const { NODE_ENV, PORT } = process.env;
export default defineConfig({
// 根据环境变量配置
publicPath: NODE_ENV === 'production' ? '/app/' : '/',
// 开发服务器端口
devServer: {
port: PORT ? parseInt(PORT) : 8000,
},
});
四、基础配置详解
4.1 应用标题 (title)
typescript
export default defineConfig({
// 设置页面标题
title: '我的应用',
// 动态标题(模板语法)
// 页面标题会是 "首页 - 我的应用"
title: false, // 禁用自动标题
});
生成结果:
html
<!-- 生成的 index.html -->
<title>我的应用</title>
4.2 路由模式 (history)
typescript
export default defineConfig({
// 三种路由模式
history: { type: 'browser' }, // 默认,HTML5 History API
history: { type: 'hash' }, // Hash 路由,URL 带 #
history: { type: 'memory' }, // 内存路由,URL 不变
});
| 模式 | URL 示例 | 适用场景 |
|---|---|---|
| browser | /users/123 |
推荐,SEO 友好,需要服务器配置 |
| hash | /#/users/123 |
静态部署,无需服务器配置 |
| memory | URL 不变 | 测试环境、Electron 应用 |
服务器配置(browser 模式需要):
nginx
# Nginx 配置
location / {
try_files $uri $uri/ /index.html;
}
4.3 公共路径 (publicPath)
typescript
export default defineConfig({
// 静态资源公共路径
publicPath: '/', // 默认,部署在根路径
publicPath: '/app/', // 部署在子路径
publicPath: './', // 相对路径,灵活部署
publicPath: 'https://cdn.example.com/', // CDN 部署
});
影响范围:
html
<!-- publicPath: '/app/' 时 -->
<script src="/app/umi.abc123.js"></script>
<link rel="stylesheet" href="/app/umi.def456.css">
<!-- publicPath: 'https://cdn.example.com/' 时 -->
<script src="https://cdn.example.com/umi.abc123.js"></script>
动态 publicPath(根据环境):
typescript
const { NODE_ENV } = process.env;
export default defineConfig({
// 开发环境用根路径,生产环境用相对路径
publicPath: NODE_ENV === 'development' ? '/' : './',
});
4.4 输出目录 (outputPath)
typescript
export default defineConfig({
// 构建产物输出目录
outputPath: 'dist', // 默认
outputPath: 'build', // 改为 build
outputPath: 'output/app', // 嵌套目录
});
注意: outputPath 不能是 src、public、node_modules 等保留目录。
4.5 文件 hash (hash)
typescript
export default defineConfig({
// 开启文件 hash
hash: true, // 开启
hash: false, // 关闭(默认)
});
开启前:
dist/
├── umi.js
├── umi.css
└── index.html
开启后:
dist/
├── umi.abc123def456.js ← 文件名带 hash
├── umi.789xyz012.css
└── index.html
为什么要用 hash?
| 好处 | 说明 |
|---|---|
| 缓存控制 | 文件内容变化 → hash 变化 → 强制更新 |
| 长期缓存 | 可以设置很长的 Cache-Control |
| 并行加载 | 不同页面可以并行加载不同 chunk |
4.6 代码压缩配置
typescript
export default defineConfig({
// JS 压缩
jsMinifier: 'terser', // 默认,使用 Terser
jsMinifier: 'esbuild', // 更快,但兼容性稍差
jsMinifier: 'swc', // Rust 实现,最快
jsMinifierOptions: {
// Terser 配置
compress: {
drop_console: true, // 移除 console
drop_debugger: true, // 移除 debugger
},
},
// CSS 压缩
cssMinifier: 'cssnano', // 默认
cssMinifier: 'esbuild', // 更快
cssMinifierOptions: {
// cssnano 配置
preset: ['advanced', { discardComments: { removeAll: true } }],
},
});
五、路由配置详解
5.1 约定式路由(默认)
不配置 routes 时,Umi 自动扫描 pages/ 目录生成路由:
src/pages/
├── index.tsx → /
├── users.tsx → /users
├── users/
│ ├── index.tsx → /users
│ ├── list.tsx → /users/list
│ └── [id].tsx → /users/:id
└── posts/
└── [id]/
└── comments.tsx → /posts/:id/comments
5.2 配置式路由
显式配置 routes 后,约定式路由失效:
typescript
export default defineConfig({
routes: [
{
path: '/',
component: 'index',
title: '首页',
},
{
path: '/users',
component: 'users',
title: '用户列表',
},
{
path: '/users/:id',
component: 'users/detail',
title: '用户详情',
},
{
path: '/settings',
component: 'settings',
title: '设置',
// 路由守卫
wrappers: ['@/wrappers/auth'],
},
{
path: '*',
component: '404',
title: '页面不存在',
},
],
});
5.3 路由配置项详解
typescript
interface RouteConfig {
// ─── 基础配置 ───
path: string; // 路由路径
component: string; // 组件路径(相对于 src/pages)
exact?: boolean; // 是否精确匹配(默认 true)
// ─── 页面信息 ───
title?: string; // 页面标题
name?: string; // 路由名称(用于菜单)
icon?: string; // 菜单图标
// ─── 布局相关 ───
layout?: boolean; // 是否使用全局布局(默认 true)
wrappers?: string[]; // 路由守卫
// ─── 权限相关 ───
access?: string; // 权限标识
redirect?: string; // 重定向
// ─── 嵌套路由 ───
routes?: RouteConfig[]; // 子路由
// ─── 其他 ───
hideInMenu?: boolean; // 隐藏菜单
hideChildrenInMenu?: boolean; // 隐藏子菜单
parentKeys?: string[]; // 父级菜单 keys
}
5.4 嵌套路由配置
typescript
export default defineConfig({
routes: [
{
path: '/users',
component: 'users/layout',
routes: [
{ path: '/users', component: 'users/index', exact: true },
{ path: '/users/list', component: 'users/list' },
{ path: '/users/:id', component: 'users/detail' },
],
},
],
});
对应的目录结构:
src/pages/
└── users/
├── layout.tsx ← 父路由组件,包含 <Outlet />
├── index.tsx ← /users
├── list.tsx ← /users/list
└── detail.tsx ← /users/:id
5.5 路由守卫 (wrappers)
typescript
export default defineConfig({
routes: [
{
path: '/admin',
component: 'admin/index',
wrappers: ['@/wrappers/auth'], // 守卫组件
},
],
});
tsx
// src/wrappers/auth.tsx
import { Outlet, Navigate } from 'umi';
export default function AuthWrapper() {
const isLoggedIn = localStorage.getItem('token');
if (!isLoggedIn) {
// 未登录,重定向到登录页
return <Navigate to="/login" replace />;
}
// 已登录,渲染子路由
return <Outlet />;
}
5.6 动态路由参数
typescript
// 路由配置
{ path: '/users/:id', component: 'users/detail' }
// 页面组件获取参数
import { useParams } from 'umi';
export default function UserDetail() {
const { id } = useParams<{ id: string }>();
return <div>用户 ID: {id}</div>;
}
5.7 路由排除(约定式路由)
typescript
export default defineConfig({
// 排除特定文件,不生成路由
conventionRoutes: {
exclude: [
/\/components\//, // 排除 components 目录
/\/models\//, // 排除 models 目录
/\/services\//, // 排除 services 目录
/_/, // 排除下划线开头的文件
],
},
});
六、代理配置详解
6.1 为什么需要代理?
开发环境下,前端和后端通常运行在不同端口:
前端:http://localhost:8000
后端:http://localhost:3000
直接请求会遇到跨域问题:
javascript
// 前端代码
fetch('http://localhost:3000/api/users') // ❌ 跨域错误
6.2 基础代理配置
typescript
export default defineConfig({
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
},
},
});
效果:
javascript
// 前端请求
fetch('/api/users')
// 实际转发到
// http://localhost:3000/api/users
6.3 完整代理配置
typescript
export default defineConfig({
proxy: {
// 代理配置 1:基础 API
'/api': {
target: 'http://localhost:3000',
changeOrigin: true, // 修改请求头的 origin
secure: false, // 允许自签名证书
ws: true, // 支持 WebSocket
headers: {
// 自定义请求头
'X-Proxy-By': 'Umi Dev Server',
},
},
// 代理配置 2:带路径重写
'/v1/api': {
target: 'http://localhost:3000',
changeOrigin: true,
pathRewrite: { '^/v1/api': '/api' }, // 重写路径
},
// 代理配置 3:带路径过滤
'/api-v2': {
target: 'http://localhost:3001',
changeOrigin: true,
// 只代理特定路径
context: (path) => {
return path.includes('/api-v2/public');
},
},
// 代理配置 4:带响应拦截
'/api-v3': {
target: 'http://localhost:3002',
changeOrigin: true,
onProxyRes: (proxyRes, req, res) => {
// 修改响应头
proxyRes.headers['x-proxy-time'] = Date.now().toString();
},
onProxyReq: (proxyReq, req, res) => {
// 修改请求
proxyReq.setHeader('x-forwarded-by', 'umi');
},
onError: (err, req, res) => {
// 错误处理
console.error('Proxy error:', err);
res.writeHead(500);
res.end('Proxy Error');
},
},
},
});
6.4 代理配置项详解
typescript
interface ProxyConfig {
// ─── 基础配置 ───
target: string; // 目标服务器地址
changeOrigin?: boolean; // 是否修改 origin(默认 false)
secure?: boolean; // 是否验证 SSL(默认 true)
ws?: boolean; // 是否代理 WebSocket(默认 false)
// ─── 路径处理 ───
pathRewrite?: Record<string, string>; // 路径重写
pathFilter?: string | string[] | Function; // 路径过滤
// ─── 请求/响应处理 ───
headers?: Record<string, string>; // 自定义请求头
onProxyReq?: Function; // 请求拦截
onProxyRes?: Function; // 响应拦截
onError?: Function; // 错误处理
// ─── 其他 ───
timeout?: number; // 超时时间(毫秒)
proxyTimeout?: number; // 代理超时时间
cookieDomainRewrite?: string | Record<string, string>; // Cookie 域名重写
}
6.5 独立代理配置文件
typescript
// config/proxy.ts
export default {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
},
'/ws': {
target: 'ws://localhost:3000',
ws: true,
},
};
// .umirc.ts
import proxy from './config/proxy';
export default defineConfig({
proxy,
});
6.6 常见代理场景
场景 1:代理到 Spring Boot 后端
typescript
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
pathRewrite: { '^/api': '' }, // 移除 /api 前缀
},
}
场景 2:代理到不同后端服务
typescript
proxy: {
'/user-api': {
target: 'http://user-service:8081',
changeOrigin: true,
pathRewrite: { '^/user-api': '/api' },
},
'/order-api': {
target: 'http://order-service:8082',
changeOrigin: true,
pathRewrite: { '^/order-api': '/api' },
},
}
场景 3:开发/生产环境区分
typescript
const { NODE_ENV } = process.env;
const proxyConfig = {
development: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
},
},
production: {}, // 生产环境不代理
};
export default defineConfig({
proxy: proxyConfig[NODE_ENV as 'development' | 'production'] || {},
});
七、插件配置详解
7.1 插件是什么?
Umi 的核心是一个空壳,所有功能通过插件实现:
┌─────────────────────────────────────────────────────────────┐
│ Umi Core │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Plugin System │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ antd │ │ request │ │ model │ ... │ │
│ │ │ 插件 │ │ 插件 │ │ 插件 │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
7.2 启用插件
typescript
export default defineConfig({
plugins: [
// 官方插件
'@umijs/plugins/dist/antd', // Ant Design
'@umijs/plugins/dist/request', // 请求封装
'@umijs/plugins/dist/model', // 数据流
'@umijs/plugins/dist/locale', // 国际化
'@umijs/plugins/dist/access', // 权限
'@umijs/plugins/dist/micro', // 微前端
'@umijs/plugins/dist/dva', // Dva(兼容)
// 本地插件
'./plugins/my-plugin.ts',
],
});
7.3 Ant Design 插件
typescript
export default defineConfig({
plugins: ['@umijs/plugins/dist/antd'],
antd: {
// ─── 主题配置 ───
theme: {
token: {
colorPrimary: '#3370FF', // 主色
colorSuccess: '#52c41a', // 成功色
colorWarning: '#faad14', // 警告色
colorError: '#f5222d', // 错误色
colorInfo: '#1890ff', // 信息色
borderRadius: 6, // 圆角
fontSize: 14, // 字号
},
algorithm: 'dark', // 暗色主题算法
},
// ─── 按需加载 ───
import: true, // 开启按需加载(默认 true)
// ─── 样式配置 ───
style: 'less', // 使用 less(默认)
// style: false, // 不引入样式(需要手动引入)
// ─── 其他配置 ───
// 移除 antd 默认的 console
compact: true,
},
});
使用 Ant Design 组件:
tsx
// 无需手动导入 antd 样式,插件自动处理
import { Button, Input, Table } from 'antd';
export default function MyPage() {
return <Button type="primary">按钮</Button>;
}
7.4 Request 插件
typescript
export default defineConfig({
plugins: ['@umijs/plugins/dist/request'],
request: {},
});
配置请求拦截(app.tsx):
tsx
// src/app.tsx
import type { RequestConfig } from 'umi';
import { message } from 'antd';
export const request: RequestConfig = {
timeout: 30000,
// 基础 URL
baseURL: '/api',
// 请求拦截器
requestInterceptors: [
(url, options) => {
const token = localStorage.getItem('token');
return {
url,
options: {
...options,
headers: {
...options.headers,
Authorization: `Bearer ${token}`,
},
},
};
},
],
// 响应拦截器
responseInterceptors: [
[
(response) => {
const { data } = response;
if (data.code !== 200) {
message.error(data.message || '请求失败');
}
return response;
},
(error) => {
message.error(error.message || '网络错误');
return Promise.reject(error);
},
],
],
};
使用请求:
tsx
import { request } from 'umi';
// GET 请求
const users = await request('/users');
// POST 请求
const result = await request('/users', {
method: 'POST',
data: { name: 'Tom' },
});
7.5 Model 插件(数据流)
typescript
export default defineConfig({
plugins: ['@umijs/plugins/dist/model'],
model: {},
});
创建 Model:
tsx
// src/models/userModel.ts
import { useState, useCallback } from 'umi';
export default function useUserModel() {
const [user, setUser] = useState<API.User | null>(null);
const [loading, setLoading] = useState(false);
const login = useCallback(async (username: string, password: string) => {
setLoading(true);
try {
const res = await request('/api/login', {
method: 'POST',
data: { username, password },
});
setUser(res.data);
return res;
} finally {
setLoading(false);
}
}, []);
const logout = useCallback(() => {
setUser(null);
localStorage.removeItem('token');
}, []);
return {
user,
loading,
login,
logout,
};
}
使用 Model:
tsx
import { useModel } from 'umi';
function LoginPage() {
const { user, loading, login } = useModel('userModel');
const handleSubmit = async (values) => {
await login(values.username, values.password);
};
return (
<Form onFinish={handleSubmit}>
{/* ... */}
<Button loading={loading} htmlType="submit">
登录
</Button>
</Form>
);
}
7.6 Locale 插件(国际化)
typescript
export default defineConfig({
plugins: ['@umijs/plugins/dist/locale'],
locale: {
default: 'zh-CN', // 默认语言
baseSeparator: '-', // 分隔符
antd: true, // 开启 antd 国际化
baseNavigator: true, // 浏览器语言检测
title: true, // 标题国际化
useLocalStorage: true, // 本地存储
},
});
创建语言包:
typescript
// src/locales/zh-CN.ts
export default {
'welcome': '欢迎',
'login.button': '登录',
'login.username': '用户名',
'login.password': '密码',
};
// src/locales/en-US.ts
export default {
'welcome': 'Welcome',
'login.button': 'Login',
'login.username': 'Username',
'login.password': 'Password',
};
使用国际化:
tsx
import { useIntl, FormattedMessage, setLocale } from 'umi';
function LoginPage() {
const intl = useIntl();
return (
<div>
{/* 方式一:FormattedMessage 组件 */}
<h1><FormattedMessage id="welcome" /></h1>
{/* 方式二:intl.formatMessage 函数 */}
<Button>{intl.formatMessage({ id: 'login.button' })}</Button>
{/* 切换语言 */}
<Button onClick={() => setLocale('en-US')}>English</Button>
<Button onClick={() => setLocale('zh-CN')}>中文</Button>
</div>
);
}
7.7 Access 插件(权限)
typescript
export default defineConfig({
plugins: ['@umijs/plugins/dist/access'],
access: {},
});
定义权限:
tsx
// src/access.ts
export default function access(initialState: { user?: API.User }) {
const { user } = initialState || {};
return {
// 是否已登录
isLoggedIn: !!user,
// 是否是管理员
isAdmin: user?.role === 'admin',
// 是否有某个权限
canDeleteUser: (userId: number) => {
return user?.role === 'admin' || user?.id === userId;
},
};
}
使用权限:
tsx
import { useAccess, Access } from 'umi';
function UserPage() {
const access = useAccess();
return (
<div>
{/* 条件渲染 */}
{access.isAdmin && <Button>删除用户</Button>}
{/* 或使用 Access 组件 */}
<Access accessible={access.isLoggedIn} fallback={<div>请登录</div>}>
<div>已登录内容</div>
</Access>
</div>
);
}
八、构建配置详解
8.1 开发环境配置
typescript
export default defineConfig({
// ─── 开发服务器 ───
devServer: {
host: 'localhost', // 监听地址
port: 8000, // 端口
https: false, // 是否 HTTPS
hot: true, // 热更新
compress: true, // Gzip 压缩
headers: { // 响应头
'X-Custom-Header': 'Umi',
},
},
// ─── 热更新配置 ───
hmr: {
include: ['src/**/*'], // 监听范围
exclude: ['src/assets/**'], // 排除
},
// ─── Source Map ───
devtool: 'source-map', // 开发环境
});
8.2 生产环境配置
typescript
export default defineConfig({
// ─── 代码分割 ───
codeSplitting: {
jsStrategy: 'granularChunks', // 细粒度分割(推荐)
// jsStrategy: 'bigVendors', // 大依赖分离
},
// ─── Source Map ───
devtool: false, // 不生成(默认)
// devtool: 'source-map', // 生成 source map
// ─── 压缩配置 ───
jsMinifier: 'terser',
cssMinifier: 'cssnano',
// ─── Tree Shaking ───
// 默认开启,无需配置
// ─── 分析构建产物 ───
analyze: {
analyzerMode: 'server', // 启动服务器查看
analyzerPort: 8888, // 端口
openAnalyzer: true, // 自动打开
},
});
8.3 Webpack 链式配置
typescript
export default defineConfig({
// 修改 Webpack 配置
chainWebpack(config) {
// ─── 添加别名 ───
config.resolve.alias
.set('@utils', require('path').resolve(__dirname, 'src/utils'))
.set('@api', require('path').resolve(__dirname, 'src/services'));
// ─── 修改图片处理 ───
config.module
.rule('images')
.test(/\.(png|jpe?g|gif|webp)$/)
.use('url-loader')
.loader('url-loader')
.options({
limit: 10240, // 10KB 以下转 base64
name: 'static/[name].[hash:8].[ext]',
});
// ─── 添加插件 ───
config.plugin('my-plugin').use(MyPlugin, [{ option: true }]);
// ─── 移除插件 ───
config.plugins.delete('progress');
// ─── 修改环境变量 ───
config.plugin('define').tap((args) => {
args[0]['process.env'].MY_VAR = JSON.stringify('value');
return args;
});
},
});
8.4 Head 配置
typescript
export default defineConfig({
// ─── HTML Head 配置 ───
headScripts: [
// 字符串形式
'console.log("Hello");',
// 对象形式
{
src: 'https://cdn.example.com/script.js',
async: true,
defer: true,
},
],
headLinks: [
{
rel: 'icon',
href: '/favicon.ico',
},
{
rel: 'stylesheet',
href: 'https://cdn.example.com/style.css',
},
],
metas: [
{ name: 'description', content: 'My Application' },
{ name: 'keywords', content: 'umi, react' },
{ charset: 'utf-8' },
{ 'http-equiv': 'X-UA-Compatible', content: 'IE=edge' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
],
links: [
{ rel: 'stylesheet', href: '/global.css' },
],
scripts: [
// body 结尾的脚本
{
src: 'https://cdn.example.com/analytics.js',
defer: true,
},
],
});
生成的 HTML:
html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="My Application">
<meta name="keywords" content="umi, react">
<link rel="icon" href="/favicon.ico">
<link rel="stylesheet" href="https://cdn.example.com/style.css">
<script>console.log("Hello");</script>
<script src="https://cdn.example.com/script.js" async defer></script>
</head>
<body>
<div id="root"></div>
<script src="/umi.js"></script>
<script src="https://cdn.example.com/analytics.js" defer></script>
</body>
</html>
九、环境变量配置
9.1 define 配置
typescript
export default defineConfig({
define: {
// 定义全局变量
'process.env.API_URL': JSON.stringify('https://api.example.com'),
'process.env.VERSION': JSON.stringify(require('./package.json').version),
// 自定义变量
'window.MY_CONFIG': {
theme: 'light',
},
},
});
使用:
tsx
console.log(process.env.API_URL);
console.log(process.env.VERSION);
console.log(window.MY_CONFIG.theme);
9.2 环境变量文件
bash
# .env(默认)
API_URL=https://api.example.com
VERSION=1.0.0
# .env.development
API_URL=http://localhost:3000
# .env.production
API_URL=https://api.example.com
typescript
// .umirc.ts
export default defineConfig({
define: {
'process.env.API_URL': JSON.stringify(process.env.API_URL),
'process.env.VERSION': JSON.stringify(process.env.VERSION),
},
});
9.3 区分环境配置
typescript
// .umirc.ts
import { defineConfig } from 'umi';
const { NODE_ENV } = process.env;
const config = {
development: {
title: '开发环境',
publicPath: '/',
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
},
},
},
production: {
title: '生产环境',
publicPath: './',
hash: true,
},
};
export default defineConfig({
title: config[NODE_ENV as keyof typeof config].title,
publicPath: config[NODE_ENV as keyof typeof config].publicPath,
...config[NODE_ENV as keyof typeof config],
});
十、完整配置示例
typescript
// .umirc.ts
import { defineConfig } from 'umi';
import proxy from './config/proxy';
const { NODE_ENV } = process.env;
export default defineConfig({
// ═════════════════════════════════════════════════════════
// 一、基础配置
// ═════════════════════════════════════════════════════════
// 应用标题
title: 'DGP 数据治理平台',
// 路由模式
history: { type: 'hash' },
// 公共路径
publicPath: NODE_ENV === 'development' ? '/' : './',
// 输出目录
outputPath: 'dist',
// 文件 hash
hash: true,
// ═════════════════════════════════════════════════════════
// 二、路由配置
// ═════════════════════════════════════════════════════════
routes: [
{ path: '/', component: 'index' },
{ path: '/assets', component: 'AssetCatalog' },
{ path: '/datasources', component: 'DataSource' },
{ path: '*', component: '404' },
],
// 约定式路由排除
conventionRoutes: {
exclude: [/\/components\//, /\/models\//, /\/services\//],
},
// ═════════════════════════════════════════════════════════
// 三、插件配置
// ═════════════════════════════════════════════════════════
plugins: [
'@umijs/plugins/dist/antd',
'@umijs/plugins/dist/request',
'@umijs/plugins/dist/model',
'@umijs/plugins/dist/locale',
'@umijs/plugins/dist/access',
],
antd: {
theme: {
token: {
colorPrimary: '#3370FF',
borderRadius: 6,
},
},
},
request: {},
model: {},
locale: {
default: 'zh-CN',
antd: true,
},
access: {},
// ═════════════════════════════════════════════════════════
// 四、代理配置
// ═════════════════════════════════════════════════════════
proxy,
// ═════════════════════════════════════════════════════════
// 五、构建配置
// ═════════════════════════════════════════════════════════
// 代码分割
codeSplitting: {
jsStrategy: 'granularChunks',
},
// JS 压缩
jsMinifier: 'terser',
jsMinifierOptions: {
compress: {
drop_console: NODE_ENV === 'production',
drop_debugger: true,
},
},
// CSS 压缩
cssMinifier: 'cssnano',
// 开发服务器
devServer: {
port: 8000,
},
// Source Map
devtool: NODE_ENV === 'development' ? 'source-map' : false,
// ═════════════════════════════════════════════════════════
// 六、环境变量
// ═════════════════════════════════════════════════════════
define: {
'process.env': {
NODE_ENV: NODE_ENV || 'development',
},
},
// ═════════════════════════════════════════════════════════
// 七、HTML 配置
// ═════════════════════════════════════════════════════════
metas: [
{ name: 'description', content: 'DGP 数据治理平台' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
],
links: [
{ rel: 'icon', href: '/favicon.ico' },
],
});
十一、配置调试技巧
11.1 打印最终配置
bash
umi dev --print-config
11.2 查看生成的文件
bash
# 查看 .umi 目录
ls -la src/.umi/
# 查看路由配置
cat src/.umi/core/route.tsx
# 查看入口文件
cat src/.umi/umi.ts
# 查看配置
cat src/.umi/core/config.ts
11.3 配置文件语法检查
bash
# 使用 TypeScript 检查
npx tsc --noEmit .umirc.ts
十二、常见问题
Q1: 配置修改后不生效?
bash
# 清理缓存重启
rm -rf src/.umi
umi dev
Q2: 如何查看 Webpack 配置?
typescript
export default defineConfig({
chainWebpack(config) {
console.log(config.toConfig());
},
});
Q3: 配置文件支持哪些语法?
- TypeScript(推荐)
- JavaScript
- 支持 import、async/await
- 支持 process.env
Q4: 如何拆分配置文件?
typescript
// config/config.ts
import routes from './routes';
import proxy from './proxy';
import theme from './theme';
export default {
routes,
proxy,
antd: { theme },
};
总结
配置优先级
用户配置 (.umirc.ts) > 插件修改 > 默认配置
配置分类速查
| 分类 | 配置项 |
|---|---|
| 基础 | title, history, publicPath, outputPath, hash |
| 路由 | routes, conventionRoutes |
| 插件 | plugins, antd, request, model, locale, access |
| 代理 | proxy |
| 构建 | codeSplitting, jsMinifier, cssMinifier, devtool |
| 开发 | devServer, hmr |
| 环境 | define |
| HTML | headScripts, headLinks, metas, scripts |