什么是Umi
是可扩展的企业级前端应用框架。Umi 以路由为基础的,同时支持配置式路由和约定式路由,保证路由的功能完备,并以此进行功能扩展。然后配以生命周期完善的插件体系,覆盖从源码到构建产物的每个生命周期,支持各种功能扩展和业务需求。
- 🎉 可扩展,Umi 实现了完整的生命周期,并使其插件化,Umi 内部功能也全由插件完成。此外还支持插件和插件集,以满足功能和垂直域的分层需求。
- 📦 开箱即用,Umi 内置了路由、构建、部署、测试等,仅需一个依赖即可上手开发。并且还提供针对 React 的集成插件集,内涵丰富的功能,可满足日常 80% 的开发需求。
- 🐠 企业级,经蚂蚁内部 3000+ 项目以及阿里、优酷、网易、飞猪、口碑等公司项目的验证,值得信赖。
- 🚀 大量自研,包含微前端、组件打包、文档工具、请求库、hooks 库、数据流等,满足日常项目的周边需求。
- 🌴 完备路由,同时支持配置式路由和约定式路由,同时保持功能的完备性,比如动态路由、嵌套路由、权限路由等等。
- 🚄 面向未来,在满足需求的同时,我们也不会停止对新技术的探索。比如 dll 提速、modern mode、webpack@5、自动化 external、bundler less 等等。
官网这张图挺不错的
开始使用
安装
# 新建文件夹
mkdir dirname && cd dirname
# 拉取初始文件
yarn create @umijs/umi-app
# 安装依赖
yanr
# 运行
yarn start
打开浏览器,访问localhost:8000端口(默认8000,如果被占用会依次往后寻找)
目录结构
├── package.json # 包管理文件
├── .umirc.ts # umi配置文件
├── .env # 环境变量设置
├── dist # 打包产物目录
├── mock # mock数据目录
├── public # 静态资源目录
└── src # 代码主目录
├── .umi # 临时文件,每次打包后会重新生成
├── layouts/index.tsx # 布局
├── pages # 页面
├── index.less
└── index.tsx
└── app.ts # 运行时配置文件
除了.umirc.ts可作为配置文件之外,可以新建.umirc.local.ts配置文件,local只有在dev环境下才会生效,并且会和.umirc.ts进行合并
app.ts是运行时配置文件,主要的作用时在运行时修改一些既有的配置
例如:使用patchRoutes
来修改在路由配置中约定的路由
// 在路由配置最前面加一个foo路由
export function patchRoutes({ routes }) {
routes.unshift({
path: '/foo',
exact: true,
component: require('@/extraRoutes/foo').default,
});
}
使用render
来覆盖默认的渲染方法,可以用于权限检测等
import { history } from 'umi';
export function render(oldRender) {
fetch('/api/auth').then(auth => {
if (auth.isLogin) { oldRender() }
else {
history.push('/login');
oldRender()
}
});
}
使用onRouteChange
可以在初始加载或路由变化时进行操作,如埋点、设置标题等
export function onRouteChange({ location, routes, action }) {
bacon(location.pathname);
}
export function onRouteChange({ matchedRoutes }) {
if (matchedRoutes.length) {
document.title = matchedRoutes[matchedRoutes.length - 1].route.title || '';
}
}
路由
现代前段开发大部分都是单页应用了,单页应用的基础就在于前端路由,页面的跳转都在浏览器完成,不会再次请求HTML,后续所有的HTML都是JS生成的
在Umi中有两种模式的路由,一种是基于约定、一种是基于配置,所谓约定就是根据事先规定好的文件位置自动生成路由文件,配置就是实现在配置文件中声明路由及对应的页面位置。
路由配置如下
const routes: IRoute[] = [
{
path: '/',
exact: true,
// redirect: '/workspace', // 跳转之后就无法命中当前声明的组件
title: '首页',
component: '@/pages/home/index',
wappers: [], // 高阶路由组件
},{
path: '/device/detail/:name',
// :name,动态路由,可以在props.match.params中获取匹配的参数
component: '@/pages/device/detail'
},{
path: '/user',
redirect: '/user/list'
routes: [
{path: '/user/list', component: '@/pages/user/list'},
{path: '/user/detail/:name', component: '@/pages/user/detail'},
]
}
]
wappers使用方法可以看一下文档,传送门
如果没有配置路由,Umi会进入约定路由模式,根据pages目录下的文件自动生成路由,例如下面的文件目录
└── pages
├── index.tsx
└── users.tsx
就等效于这样的配置路由
[
{ exact: true, path: '/', component: '@/pages/index' },
{ exact: true, path: '/users', component: '@/pages/users' },
]
以下情况不会被注册为路由
- 以
.
或_
开头的文件或目录 - 以
d.ts
结尾的类型定义文件 - 以
test.ts
、spec.ts
、e2e.ts
结尾的测试文件(适用于.js
、.jsx
和.tsx
文件) components
和component
目录utils
和util
目录- 不是
.js
、.jsx
、.ts
或.tsx
文件 - 文件内容不包含 JSX 元素
使用[]
包裹的文件可以被判定为动态路由,比如
└── pages
└── users
└── [id].tsx
└── index.tsx
等价于配置中的
[
{ exact: true, path: '/', component: '@/pages/index' },
{ exact: true, path: '/users/:id', component: '@/pages/users/[id]' },
]
创建_layout.tsx
文件可以声明嵌套路由,例如
└── pages
└── users
├── _layout.tsx
├── index.tsx
└── list.tsx
会生成
[
{ exact: false, path: '/users', component: '@/pages/users/_layout',
routes: [
{ exact: true, path: '/users', component: '@/pages/users/index' },
{ exact: true, path: '/users/list', component: '@/pages/users/list' },
]
}
]
约定路由中要使用wrappers需要在组件中添加wrappers属性,例如
import React from 'react'
function User() {
return <>user profile</>
}
User.wrappers = ['@/wrappers/auth']
// User.title = '用户中心'
// 其他路由拓展属性都可以在这里添加
export default User
页面跳转
页面跳转有声明式和命令式两种,声明式就是使用XML格式的标签来进行跳转,如
<Link to="/list">Go to list page</Link>
命令式的主要是通过history进行跳转
import { history } from 'umi';
function goToListPage() {
// 带参跳转,两种方式等价
history.push('/list?a=b');
history.push({
pathname: '/list',
query: {
a: 'b',
},
});
}
// 或者使用props中的history模块跳转
export default (props) => (
<Button
onClick={()=>props.history.push('/list');}
>Go to list page
</Button>
);