Umi 配置文件 .umirc.ts 详解

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 不能是 srcpublicnode_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
相关推荐
咖啡星人k1 小时前
用 MonkeyCode 构建全栈应用:从需求到部署的AI自动化实践
运维·人工智能·自动化
zh路西法1 小时前
【rosbridge-websocket】跨网络的ROS1与ROS2通讯法(上)
linux·网络·c++·python·websocket·网络协议
zincsweet1 小时前
Linux线程原理深度剖析:从CPU调度到pthread实现
linux·服务器
A_humble_scholar1 小时前
Linux(三)深入理解 Makefile:自动变量、增量编译原理与文件时间属性
linux·服务器·c++·makefile
团象科技1 小时前
中小出海团队运维观察:WordPress站点境外云环境搭建实操路径梳理
大数据·运维·人工智能
思麟呀1 小时前
C++11并发编程:条件变量
java·linux·jvm·c++·windows
HoneyMoose1 小时前
Jenkins Firefox 登录提示错误
运维·jenkins
江湖有缘1 小时前
Docker部署Beaver Habit Tracker习惯追踪应用
运维·docker·容器
树冰之辉1 小时前
label-studio部署方式(linux版本)
linux