跳到主要内容

Umi

UmiJS 是一个可插拔的企业级 React 应用框架,基于 React + React Router + Webpack 构建,支持约定式路由、插件体系、微前端等企业级特性。

1. 快速开始

1.1 创建项目

# 使用 npm
$ npx create-umi@latest my-app
# 或使用 yarn
$ yarn create umi my-app
# 或使用 pnpm
$ pnpm dlx create-umi@latest my-app

1.2 项目目录结构

.
├── config
│ └── config.ts # umi 配置文件
├── dist # 构建产物目录
├── mock # mock 文件目录
├── public # 静态资源目录
├── src
│ ├── .umi # dev 临时目录,需添加到 .gitignore
│ ├── .umi-production # build 临时目录,需添加到 .gitignore
│ ├── layouts # 布局目录
│ ├── models # 数据模型目录
│ ├── pages # 页面目录
│ ├── services # 后端接口服务目录
│ ├── utils # 工具函数目录
│ ├── access.ts # 权限定义文件
│ ├── app.ts # 运行时配置文件
│ └── global.less # 全局样式
└── package.json

2. 核心功能

2.1 约定式路由

// src/pages/index.tsx - 对应路由 "/"
export default function HomePage() {
return <div>Home Page</div>;
}

// src/pages/users/[id].tsx - 对应路由 "/users/:id"
export default function UserPage({ match }) {
return <div>User ID: {match.params.id}</div>;
}

// src/pages/posts/$postId.tsx - 对应路由 "/posts/:postId"
export default function PostPage({ match }) {
return <div>Post ID: {match.params.postId}</div>;
}

2.2 运行时配置

// src/app.ts
import { RequestConfig } from 'umi';

// 运行时配置
export const request: RequestConfig = {
timeout: 1000,
errorConfig: {
adaptor: (resData) => {
return {
...resData,
success: resData.ok,
errorMessage: resData.message,
};
},
},
middlewares: [
async (ctx, next) => {
const { req } = ctx;
const { url } = req;
if (url.indexOf('/api') === 0) {
req.options.headers = {
...req.options.headers,
'X-Requested-With': 'XMLHttpRequest',
};
}
await next();
},
],
};

// 修改路由
export function patchRoutes({ routes }) {
routes.unshift({
path: '/foo',
component: require('@/extraRoutes/foo').default,
});
}

// 初始数据
export async function getInitialState() {
const data = await fetchInitialData();
return data;
}

2.3 数据流管理 (Model)

// src/models/user.ts
import { Effect, ImmerReducer, Subscription } from 'umi';

export interface UserModelState {
name: string;
age: number;
}

export interface UserModelType {
namespace: 'user';
state: UserModelState;
effects: {
fetchUser: Effect;
};
reducers: {
save: ImmerReducer<UserModelState>;
};
subscriptions: {
setup: Subscription;
};
}

const UserModel: UserModelType = {
namespace: 'user',
state: {
name: '',
age: 0,
},
effects: {
*fetchUser({ payload }, { call, put }) {
const response = yield call(fetchUserFromApi, payload);
yield put({
type: 'save',
payload: response,
});
},
},
reducers: {
save(state, action) {
return {
...state,
...action.payload,
};
},
},
subscriptions: {
setup({ dispatch, history }) {
return history.listen(({ pathname }) => {
if (pathname === '/user') {
dispatch({
type: 'fetchUser',
});
}
});
},
},
};

export default UserModel;

2.4 权限管理

// src/access.ts
export default function(initialState) {
const { role } = initialState;

return {
canReadFoo: true,
canUpdateFoo: role === 'admin',
canDeleteFoo: (foo) => {
return foo.ownerId === initialState.userId;
},
};
}

// 在组件中使用
import { useAccess, Access } from 'umi';

function Foo() {
const access = useAccess();

return (
<div>
<Access
accessible={access.canReadFoo}
fallback={<div>无权限查看</div>}
>
Foo 内容
</Access>
</div>
);
}

2.5 插件系统

// config/config.ts
export default {
plugins: [
// 内置插件
'@umijs/plugins/dist/antd',
'@umijs/plugins/dist/request',

// 自定义插件
'./plugins/customPlugin',
],
antd: {
dark: true,
compact: true,
},
request: {
dataField: 'data',
},
};

// 自定义插件示例
import { IApi } from 'umi';

export default (api: IApi) => {
api.onDevCompileDone(({ isFirstCompile, stats }) => {
if (isFirstCompile) {
console.log('首次编译完成');
}
});

api.addHTMLHeadScripts(() => [
{
src: 'https://example.com/script.js',
},
]);
};

2.6 微前端

// 主应用配置
export default {
qiankun: {
master: {
apps: [
{
name: 'app1',
entry: '//localhost:7001',
props: {
onClick: (event) => console.log(event),
},
},
],
},
},
};

// 子应用配置
export default {
qiankun: {
slave: {},
},
};

3. 高级特性

3.1 动态加载组件

import { dynamic } from 'umi';

const DynamicComponent = dynamic({
loader: async () => {
const { default: Component } = await import('./Component');
return Component;
},
loading: () => <div>Loading...</div>,
});

3.2 国际化

// src/locales/zh-CN.ts
export default {
'welcome': '欢迎',
'user.name': '用户名',
};

// src/locales/en-US.ts
export default {
'welcome': 'Welcome',
'user.name': 'Username',
};

// 使用
import { useIntl, getLocale, setLocale } from 'umi';

function Component() {
const intl = useIntl();

return (
<div>
{intl.formatMessage({ id: 'welcome' })}
<select
value={getLocale()}
onChange={(e) => setLocale(e.target.value)}
>
<option value="zh-CN">中文</option>
<option value="en-US">English</option>
</select>
</div>
);
}

3.3 Mock 数据

// mock/api.ts
export default {
'GET /api/users': {
users: [
{ id: 1, name: 'foo' },
{ id: 2, name: 'bar' },
],
},
'POST /api/users/create': (req, res) => {
res.end('OK');
},
};

3.4 部署配置

// config/config.ts
export default {
// 生产环境构建配置
define: {
'process.env.API_URL': 'https://api.example.com',
},

// CDN 配置
publicPath: 'https://cdn.example.com/',

// 路由基础路径
base: '/admin/',

// 静态资源前缀
outputPath: 'dist',
hash: true,

// 压缩配置
chunks: ['vendors', 'umi'],
chainWebpack: function (config, { webpack }) {
// 自定义 webpack 配置
},
};

4. 最佳实践

4.1 目录组织

src/
├── components/ # 共享组件
├── hooks/ # 自定义 hooks
├── layouts/ # 布局组件
├── models/ # 数据模型
├── pages/ # 页面组件
├── services/ # API 服务
├── types/ # TypeScript 类型定义
└── utils/ # 工具函数

4.2 TypeScript 支持

// 页面组件类型
interface PageProps {
match: {
params: {
id: string;
};
};
}

// Model 类型
interface UserState {
name: string;
age: number;
}

// Service 类型
interface ResponseData<T> {
success: boolean;
data: T;
errorMessage?: string;
}

4.3 性能优化

  1. 代码分割
// 路由级别的代码分割
export default {
dynamicImport: {
loading: '@/components/Loading',
},
};
  1. 预加载
// 预加载其他路由
import { history } from 'umi';

// 鼠标悬停时预加载
const handleMouseEnter = () => {
history.prefetch('/about');
};
  1. 缓存优化
// 开启 hard-source-webpack-plugin
export default {
hardSource: true,
};

参考资源