介紹umi
初探
對比以往使用的 create-react-app 搭建react項目,根據(jù)需要我們還得集合webpack打包,或者引入redux狀態(tài)管理器等,而umi ---
通過 create-umi提供腳手架能力,
然后我們可以選擇需要生成的項目類型:
- app,通用項目腳手架,支持選擇是否啟用 TypeScript,以及 umi-plugin-react 包含的功能
- ant-design-pro,僅包含 ant-design-pro 布局的腳手架,具體頁面可通過 umi block 添加
- block,區(qū)塊腳手架
- plugin,插件腳手架
- library,依賴(組件)庫腳手架,基于 umi-plugin-library
之后選擇是否需要支持typescript
然后選擇需要的功能: - antd: UI框架,啟用后實現(xiàn)antd, antd-mobile 和 antd-pro 的按需編譯,無需要手動配置。
- dva: 基于 redux 和 redux-saga 的數(shù)據(jù)流方案,然后為了簡化開發(fā)體驗,dva 還額外內(nèi)置了 react-router 和 fetch,所以也可以理解為一個輕量級的應用框架
- code splitting: 是否代碼分包
- dll: 通過 webpack 的 dll 插件預打包一份 dll 文件來達到二次啟動提速的目的
按上下箭頭移動,并按 空格 鍵選中。
確定后,會根據(jù)選擇自動創(chuàng)建好目錄和文件
安裝依賴,yarn start啟動項目。
路由
umi 以路由為基礎的,支持類 next.js 的約定式路由,以及各種進階的路由功能,并以此進行功能擴展,比如支持路由級的按需加載。
(1) 無需手動配置路由
根據(jù)pages目錄自動生成路由配置,會根據(jù) src / pages 下 文件名自動生成路由
(也可以配置.umirc.js中的 routes 屬性,此配置項存在時則不會對 src/pages 目錄做約定式的解析)
(2) 其他基礎知識:
-
動態(tài)路由:帶 $ 前綴的目錄或文件。
目錄結(jié)構:+ pages/ + $post/ - index.js - comments.js + users/ $id.js - index.js會生成的路由配置:
[ { path: '/', component: './pages/index.js' }, { path: '/users/:id', component: './pages/users/$id.js' }, { path: '/:post/', component: './pages/$post/index.js' }, { path: '/:post/comments', component: './pages/$post/comments.js' }, ] 可選的動態(tài)路由:
約定動態(tài)路由如果帶 $ 后綴,則為可選動態(tài)路由。
(3)常用的路由操作
import Link from 'umi/link';
import router from 'umi/router';
普通使用
<Link to="/list">跳轉(zhuǎn)</Link>帶參數(shù)
<Link to="/list?a=b">跳轉(zhuǎn)</Link>包含子組件
<Link to="/list?a=b"><button>跳轉(zhuǎn)</button></Link>點擊跳轉(zhuǎn)
<button onClick={() => router.push('/list')}>跳轉(zhuǎn)</button>-
router.push
// 普通跳轉(zhuǎn),不帶參數(shù) router.push('/list'); // 帶參數(shù) router.push('/list?a=b'); router.push({ pathname: '/list', query: { a: 'b' } }); router.replace
router.go(n)
往前或者往后跳指定頁數(shù)。router.goBack()
后退一頁-
umi/redirect
重定向import Redirect from 'umi/redirect'; <Redirect to="/login" />; withRouter
當組件需要路由參數(shù)時,使用withRouter可以給當前組件傳入路由參數(shù),將react-router的history、location、match三個對象傳入props對象上,此時就可以使用this.props
布局
(1)全局layout
約定 src/layouts/index.js 為全局路由,返回一個 React 組件,通過 props.children 渲染子組件。
比如:
export default function(props) {
return (
<>
<Header />
{ props.children }
<Footer />
</>
);
}
(2)不同的全局layout
可以在 layouts/index.js 對 location.path 做區(qū)分,渲染不同的 layout 。
比如想要針對 /login 輸出簡單布局:
export default function(props) {
if (props.location.pathname === '/login') {
return <SimpleLayout>{ props.children }</SimpleLayout>
}
return (
<>
<Header />
{ props.children }
<Footer />
</>
);
}
(3)嘗試
要求: 登錄頁和首頁顯示不同的布局

同樣對 location.path 做區(qū)分,但是如果是動態(tài)路由或者嵌套路由這樣的匹配是有漏洞的。
優(yōu)化后:
配置路由對應的布局,默認使用NavigatorLayout
const routeLayoutMap = [{
matches: ['/users', '/login', '/contact-sale'],
component: BlankLayout,
}, {
matches:[],
component: NavigatorLayout,
}];
const res = routeLayoutMap.find(({ matches }) => {
return checkRouteMatch(matches, location.pathname)
});
const layout = res ? res.component : NavigatorLayout
return React.createElement(layout, props);
}
根據(jù)正則判斷
function checkRouteMatch (routes, pathname) {
const _routes = routes.map(one => {
if (isString(one) || isRegExp(one)) one = { match: one }
if (!isObject(one)) return {}
const { match, exclude } = one
return {
match,
exclude: (isArray(exclude) || !exclude) ? exclude : [exclude]
};
})
return _routes.some(({ match, exclude }) => {
function check (rule, value) {
if (isString(rule)) return value.includes(rule);
if (isRegExp(rule)) return rule.test(value);
return false;
}
const res = check(match, pathname)
if (!res) return false;
if (!isArray(exclude) || !exclude.length) return res;
return !exclude.some(one => check(one, pathname))
})
}
mock
用之前先把mock使用示例看看==>mock.js文檔
(1)在umi中使用mock:
-
新建mock數(shù)據(jù):
在 mock 文件下新建文件 users.js 為保存mock數(shù)據(jù)的一個文件。import mockjs from 'mockjs'; let dataSource = mockjs.mock({ 'list|15-30': [{ 'id': () => mockjs.Random.guid(), 'name': /[a-zA-Z0-9]{4,8}/, 'email': /[a-zA-Z0-9]{4,8}@test\.com/, 'website': /[a-zA-Z0-9]{4,8}/ }] }).list; export default { 'GET /api/users': (req, res) => { const { page = 0, size = 10 } = req.query const _page = parseInt(page, 10) const _size = parseInt(size, 10) const data = dataSource.slice(_page * _size, (_page + 1) * _size); res.json({ content: data, number: _page, size: _size, totalElements: dataSource.length, }) }, } 調(diào)用數(shù)據(jù)
輸入路徑可直接獲取數(shù)據(jù),:/api/users排除 mock 目錄下不作 mock 處理的文件。
在config/config.start-dev.js文件中使用exclude
mock: {
exclude: ['mock/login.js']
},