# UmiJS 实用配置

最近发现一个前端脚手架或者说前端应用框架 UmiJS (opens new window),它是阿里开发的可扩展的企业级前端应用框架,可以帮助我们快速开发 React、Vue、SSR 应用。当然主要还是 React 应用,因为它很好的集成了 Ant Design (opens new window)ProComponents (opens new window) 组件库。

# 安装

$ pnpm dlx create-umi@latest
1

它会提示您选择哪个应用模板(Pick Umi App Template),有 4 个选项:

如果想要快速开发,就选择 Ant Design Pro

# 替换 UmiJS 元素

创建完成之后,运行项目,效果如下:

我们需要替换 logo,title,favicon,删除侧边栏底部

首先把 logo 文件放在 /src/assets 目录下,然后打开 src/app.ts,替换 logo 的配置

import logo from './assets/logo.png';

export const layout = () => {
  return {
    logo: logo,
    // 其它配置
  };
};
1
2
3
4
5
6
7
8

# 修改 Title

打开 .umirc.ts,替换 title

export default defineConfig({
  layout: {
    title: 'My React App',
  },
  // 其它配置
}
1
2
3
4
5
6

也可以在 src/app.ts 里修改

# 删除侧边栏底部

打开 src/app.ts,设置 rightContentRender: false

export const layout = () => {
  return {
    rightContentRender: false,
    // 其它配置
  };
};
1
2
3
4
5
6

# 替换 favicon

src 目录下放在一个 favicon.(ico|gif|png|jpg|jpeg|svg|avif|webp) 文件,将会自动在产物中添加站点 favicon

<link rel="shortcut icon" href="/favicon.png">
1

可以使用 favicons (opens new window) 手动配置站点图标,配置值优先于约定

修改之后,效果如下:

# 定义颜色变量

# 内置颜色变量

UmiJS 内置了一组 Less 颜色变量值,比如 @primary-color

文件位置 node_modules/@umijs/max/node_modules/antd/es/style/themes/default.less,此外同一个目录下还有一个定义颜色变量值的文件,在 node_modules/@umijs/max/node_modules/antd/es/style/themes/variable.less 个人猜测是 Ant Design 5.x 组件使用的。

@primary-color: @blue-6;
1

因此可以在项目里的 Less 文件里直接使用

.container {
  padding-top: 80px;
  background-color: @primary-color;
}
1
2
3
4

# theme 配置项

UmiJS 可以通过 theme 配置项,修改内置的颜色变量或者添加新的颜色变量

// .umirc.ts
export default defineConfig({
  theme: {
    '@custom-text-color': '#0000ff',
    '@primary-color': '#ff0000',
	},
})
1
2
3
4
5
6
7

自定义颜色值时,建议添加一个前缀,可以避免与内置的颜色变量冲突

# 魔法

上面两个方法都有各自的缺点:

  • 内置的颜色变量,不方便查看有哪些颜色变量以及对应的颜色值,同时颜色值可能并不符合自己的应用
  • theme 配置项,需要在 TS 文件定义颜色变量,不方便

我们还是习惯使用 Less 文件定义颜色变量,比如在 src/styles/theme.less 文件里定义颜色变量,同时还可以导出供 JS/TS 文件使用的颜色变量。结合下面说的 Ant Desgin Token,统一了 Ant Desgin 组件和系统的颜色。

// src/styles/theme.less

@primary-color: #1664ff; // 主色调
@text-color: #1d1d20;
@success-color: #6ac21d;
@error-color: #c73131;
@warning-color: #f98e1b;

// 导出变量供 JS/TS 文件使用
:export {
  primaryColor: @primary-color;
  textColor: @text-color;
  successColor: @success-color;
  errorColor: @error-color;
  warningColor: @warning-color;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

但是别的 Less 文件使用这个 Less 文件时,需要手动导入

@import "@/styles/theme.less"
  
.page {
  color: @text-color
}
1
2
3
4
5

一个系统可能有成百上千个 Less 文件,每个都手动导入太麻烦了,幸好有人发现了一个魔法:

// .umirc.ts
export default defineConfig({
  lessLoader: {
    modifyVars: {
      hack: 'true; @import "@/styles/theme.less";',
    },
    javascriptEnabled: true,
  },
})
1
2
3
4
5
6
7
8
9

这样就不需要一个一个手动导入了。

[!CAUTION]

使用这个方法之后前面两个方法就无效了,这样也有效地避免了颜色冲突

# 定制 Ant Design 组件样式

Ant Design 5.x 使用 Design Token 配置组件样式,详情请参考 Ant Design 定制主题 (opens new window)

UmiJS 提供了两种方法定制 Ant Design 组件样式

  • 构建时配置,在 .umirc.ts 文件里配置
  • 运行时配置,在 app.{ts|tsx} 文件里配置

运行时配置的优先级大于构建时配置

# 构建时配置

构建时配置,有两个选项 antd.themeantd.configProvider.theme,前者的优先级大于后者

// .umirc.ts
export default defineConfig({
  antd: {
    theme: {
      token: {
        colorPrimary: '#00ff00',
      },
    },
  },
})

// 或者
// .umirc.ts
export default defineConfig({
  antd: {
    configProvider: {
      theme: {
        token: {
          colorPrimary: '#00ffff',
        },
      },
    }
  },
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 运行时配置

// app.ts
import { RuntimeAntdConfig } from '@umijs/max';
export const antd: RuntimeAntdConfig = (memo) => {
  memo.theme ??= {};
  memo.theme.token = {
    colorPrimary: '#ff0000', // 主题色
  };
  
  memo.appConfig = {
    message: {
      maxCount: 3,  // 配置 message 最大显示数,超过限制时,最早的消息会被自动关闭
    }
  }

  return memo;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

结合上面将的 Less 文件变量,统一了 Ant Desgin 组件和系统的颜色。

构建时配置无法引用 Less 文件变量

// app.ts
import themes from '@/styles/theme.less';
import { RuntimeAntdConfig } from '@umijs/max';
export const antd: RuntimeAntdConfig = (memo) => {
  memo.theme ??= {};
  memo.theme.token = {
    colorPrimary: themes['primaryColor'], // 主题色
  };
  
  memo.appConfig = {
    message: {
      maxCount: 3,  // 配置 message 最大显示数,超过限制时,最早的消息会被自动关闭
    }
  }

  return memo;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 图标

# Iconfont

应用使用的 SVG 小图标一般使用 iconfont (opens new window) 来管理。UmiJS 怎么使用 iconfont 呢?可以使用 Ant Design createFromIconfontCN() (opens new window) 方法封装组件

import React from 'react';
import { createFromIconfontCN } from '@ant-design/icons';

const MyIcon = createFromIconfontCN({
  scriptUrl: '//at.alicdn.com/t/font_8d5l8fzk5b87iudi.js', // 在 iconfont.cn 上生成
});
1
2
3
4
5
6

如果不想用 iconfont 的托管地址,而是下载图标文件呢?可以这样使用

import '@/iconfont/iconfont.js';
import { createFromIconfontCN } from '@ant-design/icons';

const Icon = createFromIconfontCN({
  scriptUrl: '',
});

type IconFontProps = Parameters<typeof Icon>[0];

interface MyIconProps extends IconFontProps {
  type: string;
  style?: React.CSSProperties;
  className?: string;
}

function MyIcon({ type, style, className, ...rest }: MyIconProps) {
  return <Icon {...rest} type={type} className={className} style={style} />;
}

export default MyIcon;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

其实 createFromIconfontCN() 方法就是创建并插入一个 script 标签

function createScriptUrlElements(scriptUrls) {
    var index = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
    var currentScriptUrl = scriptUrls[index];
    if (isValidCustomScriptUrl(currentScriptUrl)) {
      var script = document.createElement('script');
      script.setAttribute('src', currentScriptUrl);
      script.setAttribute('data-namespace', currentScriptUrl);
      if (scriptUrls.length > index + 1) {
        script.onload = function () {
          createScriptUrlElements(scriptUrls, index + 1);
        };
        script.onerror = function () {
          createScriptUrlElements(scriptUrls, index + 1);
        };
      }
      customCache.add(currentScriptUrl);
      document.body.appendChild(script);
    }
  }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# SVG 文件

如果 SVG 图标没有使用 iconfont 托管,直接放在工程目录下,该怎么使用呢?UmiJS 有很多方法展示 SVG 图标,比如使用 Ant Design 的 Icon 组件(详情请参考 自定义 Icon (opens new window))或者 svgr 配置项 (opens new window)。但是最简单的方式是把 SVG 文件放在 src/icons 目录下,然后通过 UmiJS 的 Icon 组件,使用 local:文件名 的方式访问。

// src/icons/umi.svg
import { Icon } from 'umi';
<Icon icon="local:umi" />
1
2
3

Umi JS 还可以使用其它的图标集,更多详情请参考 icon 集使用 (opens new window)

# PublicPath

如果要将前端应用部署在子路径上,比如 www.example.com/app/,在 Vue-CLI 上只需设置 publicpath 配置项 (opens new window),在 Vite 上只需要设置 base 配置项 (opens new window)。而在 UmiJS 中我们需要同时设置 base 配置项 (opens new window)publicPath 配置项 (opens new window)

打开 .umirc.ts,添加下面的配置。

const base =
  process.env.NODE_ENV === 'production' ? '/app/' : '/';

export default defineConfig({
  base: base,
  publicPath: base,
})
1
2
3
4
5
6
7

在 UmiJS 中 base 配置项,添加路由前缀,比如有路由 //users,设置 base/app/ 后就可通过 /app//app/users 访问到之前的路由。publicPath 配置项等同于 webpack 的 publicPath (opens new window),指定应用程序所有资源(js, css, assets)的基本路径。

另外,如果要添加 headScripts (opens new window),并且脚本 src 指向 public 目录下的 js 文件,UmiJS 并不会自动帮我们添加 base 前缀,所以我们需要手动添加

export default defineConfig({
  headScripts: [{
    src: `${base}/js/my.js`
  }]
})
1
2
3
4
5

# 其它

# App.message 报错

当我们使用

import { App } from 'antd';
const { message } = App;
message.error("错误")
1
2
3

报错,说没有 error 函数,这需要添加 antdappConfig,配置 antd 的 App 包裹组件。

打开 .umirc.ts,添加下面的配置。

export default defineConfig({
  antd: {
    configProvider: {},
    appConfig: {},
  }
}
1
2
3
4
5
6

更多详情请参考 Umi Max 之 antd 配置 (opens new window)

# References