umi插件開發(fā)仿dumi項(xiàng)目實(shí)現(xiàn)頁面布局詳解
實(shí)現(xiàn)思路
上一章我們已經(jīng)完成/docs目錄下文件自動生路由功能,本章我們將在此基礎(chǔ)上,實(shí)現(xiàn)自動生成頁面導(dǎo)航的功能。
- 使用默認(rèn)模板提供的layout展示路由切換
- 使用自定義主題插件
使用默認(rèn)項(xiàng)目提供的layout文件
在我們創(chuàng)建默認(rèn)umi項(xiàng)目后,會在/src/layouts下生成一個布局文件:

同時在上一章節(jié)我們打印modifyRouteshook的入?yún)ⅲ梢钥吹?code>umi會將該文件轉(zhuǎn)成一個layout router對象,如圖所示:

因此我們可以直接將這個layout的id屬性,賦值到自動生成的路由的parentId屬性上,并添加該對象到返回值中:
// /src/features/routes.ts
import path from 'path';
import type { IApi } from 'umi';
import type { IRoute } from '@umijs/core/dist/types';
import { getConventionRoutes } from '@umijs/core';
export default (api: IApi) => {
api.describe({ key: 'domi:routes' });
api.modifyRoutes((oRoutes: Record<string, IRoute>) => {
const routes: Record<string, IRoute> = {}
const docDir = 'docs'
// 獲取某個目錄下所有可以配置成umi約定路由的文件
const dirRoutes: Record<string, IRoute> = getConventionRoutes({
base: path.join(api.cwd, docDir),
});
// 默認(rèn)提供的布局layout的Id
let docLayoutId : undefined | string = '@@/global-layout';
// 從舊路由對象中獲取放入返回值中
routes[docLayoutId] = oRoutes[docLayoutId]
Object.entries(dirRoutes).forEach(([key, route]) => {
// 這里將文件的路徑改為絕對路徑,否則umi會默認(rèn)找/src/pages下組件
route.file = path.resolve(docDir, route.file);
// 給頁面對象賦值布局Id
route.parentId = docLayoutId
routes[route.id] = route;
});
return routes;
});
};
同時我們修改布局文件,將導(dǎo)航改成我們的測試頁面路由:
// /src/layouts/index.tsx
import { Link, Outlet } from 'umi';
import styles from './index.less';
export default function Layout() {
return (
<div className={styles.navs}>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/button">Button</Link></li>
</ul>
<Outlet />
</div>
);
}
運(yùn)行項(xiàng)目可以看到布局文件已添加到頁面中,并可以切換路由:

自定義主題
上面我們通過最簡單的方式使用了默認(rèn)提供的布局文件,這種方式對頁面局限性比較大,由于各個項(xiàng)目對頁面的展示要求不一樣,dumi提供了主題插件來靈活擴(kuò)展用戶自定義布局。同時提供了默認(rèn)主題,用戶可以選擇性覆蓋默認(rèn)樣式。
本節(jié)我們將實(shí)現(xiàn)其中的默認(rèn)主題加載
準(zhǔn)備工作
創(chuàng)建主題插件,并注冊到插件配置中
mkdir /src/features/theme.ts
import { defineConfig } from "umi";
export default defineConfig({
plugins: [
'./src/features/routes.ts',
'./src/features/theme.ts',
],
});
創(chuàng)建默認(rèn)主題目錄,將/src/layouts/index.tsx文件復(fù)制到這里
mkdir /src/client/theme-default/layouts/DocLayout cp /src/layouts/index.tsx /src/client/theme-default/layouts/DocLayout/index.tsx cp /src/layouts/index.less /src/client/theme-default/layouts/DocLayout/index.less
然后/src/layouts就沒用了,可以刪掉
主題插件功能
dumi主題插件主要提供的功能有:
- 加載默認(rèn)主題布局文件
- 加載國際化語言包
- 合并默認(rèn)主題及自定義主題
- ...等其他與頁面相關(guān)功能
本章我們將實(shí)現(xiàn)默認(rèn)布局的加載,并配置到路由中。
modifyAppData
umi提供modifyAppData鉤子,用于初始收集應(yīng)用數(shù)據(jù),在dumi中使用這個鉤子來初始化主題數(shù)據(jù)。
我們在主題插件中提供的主題數(shù)據(jù)后續(xù)會被用在修改路由中,即在modifyRoutes階段使用。因?yàn)?code>modifyRoutes是在appData插件的modifyAppData階段中執(zhí)行,所以通過before: 'appData讓主題插件的modifyAppData在appData插件的modifyAppData之前先初始化完成,這樣在modifyRoutes中就可以使用到主題數(shù)據(jù)。
插件代碼
// /src/features/theme.ts
import path from 'path';
import type { IApi } from 'umi';
import { glob, winPath } from 'umi/plugin-utils';
const DEFAULT_THEME_PATH = path.join(__dirname, '../../src/client/theme-default');
export default async(api: IApi) => {
api.describe({ key: 'domi:theme' });
api.modifyAppData({
before: 'appData',
async fn(memo: any) {
const defaultThemeData = loadTheme(DEFAULT_THEME_PATH);
// @ts-ignore
api.service.themeData = defaultThemeData
return memo;
},
});
}
/**
* 加載主題信息
*/
function loadTheme(dir: string) {
return {
name: path.basename(dir),
path: dir,
layouts: getComponentMapFromDir(
'layouts/{GlobalLayout,DocLayout,DemoLayout}{.,/index.}{js,jsx,ts,tsx}',
dir,
),
};
};
/**
* 提取dir目錄下符合條件的組件信息
*/
function getComponentMapFromDir(globExp: string, dir: string) {
return glob
.sync(globExp, { cwd: dir })
.reduce<any>((ret, file) => {
const specifier = path.basename(
winPath(file).replace(/(\/index)?\.[a-z]+$/, ''),
);
// ignore non-component files
if (/^[A-Z\d]/.test(specifier)) {
ret[specifier] = {
specifier,
source: winPath(path.join(dir, file)),
};
}
return ret;
}, {});
}
另一個比較不優(yōu)雅的地方是這里使用了api.service來存儲生成的主題數(shù)據(jù),同樣因?yàn)樯厦嫣岬降碾A段問題,modifyRoutes是在modifyAppData中執(zhí)行,所以這里只能用全局變量來存儲,否則在修改路由階段拿不到這里的數(shù)據(jù)。
執(zhí)行完成后,api.service.themeData就得到了主題相關(guān)的數(shù)據(jù):
{
name: 'theme-default',
path: 'D:\\project\\domi\\src\\client\\theme-default',
layouts: {
DocLayout: {
specifier: 'DocLayout',
source: 'D:/project/domi/src/client/theme-default/layouts/DocLayout/index.tsx'
}
}
}
生成layout路由對象
前面我們使用了模板自帶的對象@@/global-layout作為布局模板,現(xiàn)在我們可以將它改成動態(tài)添加主題布局。
我們直接在路由插件中使用主題插件中生成的布局?jǐn)?shù)據(jù),代碼很簡單,根據(jù)前面的layout來生成一個布局路由,并添加到返回值中即可,這樣所有parentId為DocLayout.specifier的頁面就能使用該布局了。
// /src/features/routes.ts
...
let docLayoutId : undefined | string = undefined;
// @ts-ignore
const { DocLayout } = api.service.themeData.layouts;
// 從舊路由對象中獲取放入返回值中
if (DocLayout) {
docLayoutId = DocLayout.specifier;
routes[DocLayout.specifier] = {
id: DocLayout.specifier,
path: '/',
file: DocLayout.source,
parentId: undefined,
absPath: '/',
isLayout: true,
};
}
...
使用同步偽代碼來描述上面流程
// /umi/packages/core/src/service/service.ts 中代碼
service.collectAppData() {
// /src/features/theme.ts中代碼
// 配置before: 'appData' 使其先于appData.modifyAppData執(zhí)行
themePlugin.modifyAppData() {
// 這里加載主題數(shù)據(jù)
api.service.themeData = loadTheme(DEFAULT_THEME_PATH);
}
// /umi/packages/preset-umi/src/features/appData/appData.ts 中代碼
appDataPlugin.modifyAppData() {
// /src/features/routes.ts中代碼
routesPlugin.modifyRoutes() {
// 這里使用主題數(shù)據(jù)生成布局路由
const { DocLayout } = api.service.themeData.layouts;
routes[DocLayout.specifier] = {
...
isLayout: true,
};
}
}
}
運(yùn)行檢查
至此我們已經(jīng)完成生成默認(rèn)布局,實(shí)現(xiàn)了簡易的主題插件,運(yùn)行代碼可以看到和上節(jié)運(yùn)行結(jié)果一樣:

路由和頁面問題基本解決了,接下來就要開始正式解析markdown文件。
以上就是umi插件開發(fā)仿dumi項(xiàng)目實(shí)現(xiàn)頁面布局詳解的詳細(xì)內(nèi)容,更多關(guān)于umi插件仿dumi頁面布局的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
本地存儲localStorage設(shè)置過期時間示例詳解
這篇文章主要為大家介紹了本地存儲localStorage設(shè)置過期時間示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
精確到按鈕級別前端權(quán)限管理實(shí)現(xiàn)方案
這篇文章主要為大家介紹了精確到按鈕級別前端權(quán)限管理實(shí)現(xiàn)方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
JavaScript自動化測試添加頁面DOM元素唯一ID方案示例
這篇文章主要為大家介紹了JavaScript自動化測試添加頁面DOM元素唯一ID方案示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09

