umi插件開(kāi)發(fā)仿dumi項(xiàng)目實(shí)現(xiàn)頁(yè)面布局詳解
實(shí)現(xiàn)思路
上一章我們已經(jīng)完成/docs
目錄下文件自動(dòng)生路由功能,本章我們將在此基礎(chǔ)上,實(shí)現(xiàn)自動(dòng)生成頁(yè)面導(dǎo)航的功能。
- 使用默認(rèn)模板提供的layout展示路由切換
- 使用自定義主題插件
使用默認(rèn)項(xiàng)目提供的layout文件
在我們創(chuàng)建默認(rèn)umi
項(xiàng)目后,會(huì)在/src/layouts
下生成一個(gè)布局文件:
同時(shí)在上一章節(jié)我們打印modifyRoutes
hook的入?yún)ⅲ梢钥吹?code>umi會(huì)將該文件轉(zhuǎn)成一個(gè)layout router對(duì)象,如圖所示:
因此我們可以直接將這個(gè)layout的id
屬性,賦值到自動(dòng)生成的路由的parentId
屬性上,并添加該對(duì)象到返回值中:
// /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' // 獲取某個(gè)目錄下所有可以配置成umi約定路由的文件 const dirRoutes: Record<string, IRoute> = getConventionRoutes({ base: path.join(api.cwd, docDir), }); // 默認(rèn)提供的布局layout的Id let docLayoutId : undefined | string = '@@/global-layout'; // 從舊路由對(duì)象中獲取放入返回值中 routes[docLayoutId] = oRoutes[docLayoutId] Object.entries(dirRoutes).forEach(([key, route]) => { // 這里將文件的路徑改為絕對(duì)路徑,否則umi會(huì)默認(rèn)找/src/pages下組件 route.file = path.resolve(docDir, route.file); // 給頁(yè)面對(duì)象賦值布局Id route.parentId = docLayoutId routes[route.id] = route; }); return routes; }); };
同時(shí)我們修改布局文件,將導(dǎo)航改成我們的測(cè)試頁(yè)面路由:
// /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)目可以看到布局文件已添加到頁(yè)面中,并可以切換路由:
自定義主題
上面我們通過(guò)最簡(jiǎn)單的方式使用了默認(rèn)提供的布局文件,這種方式對(duì)頁(yè)面局限性比較大,由于各個(gè)項(xiàng)目對(duì)頁(yè)面的展示要求不一樣,dumi
提供了主題插件來(lái)靈活擴(kuò)展用戶自定義布局。同時(shí)提供了默認(rèn)主題,用戶可以選擇性覆蓋默認(rèn)樣式。
本節(jié)我們將實(shí)現(xiàn)其中的默認(rèn)主題加載
準(zhǔn)備工作
創(chuàng)建主題插件,并注冊(cè)到插件配置中
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
就沒(méi)用了,可以刪掉
主題插件功能
dumi
主題插件主要提供的功能有:
- 加載默認(rèn)主題布局文件
- 加載國(guó)際化語(yǔ)言包
- 合并默認(rèn)主題及自定義主題
- ...等其他與頁(yè)面相關(guān)功能
本章我們將實(shí)現(xiàn)默認(rèn)布局的加載,并配置到路由中。
modifyAppData
umi
提供modifyAppData
鉤子,用于初始收集應(yīng)用數(shù)據(jù),在dumi
中使用這個(gè)鉤子來(lái)初始化主題數(shù)據(jù)。
我們?cè)谥黝}插件中提供的主題數(shù)據(jù)后續(xù)會(huì)被用在修改路由中,即在modifyRoutes
階段使用。因?yàn)?code>modifyRoutes是在appData
插件的modifyAppData
階段中執(zhí)行,所以通過(guò)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; }, {}); }
另一個(gè)比較不優(yōu)雅的地方是這里使用了api.service
來(lái)存儲(chǔ)生成的主題數(shù)據(jù),同樣因?yàn)樯厦嫣岬降碾A段問(wèn)題,modifyRoutes
是在modifyAppData
中執(zhí)行,所以這里只能用全局變量來(lái)存儲(chǔ),否則在修改路由階段拿不到這里的數(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路由對(duì)象
前面我們使用了模板自帶的對(duì)象@@/global-layout
作為布局模板,現(xiàn)在我們可以將它改成動(dòng)態(tài)添加主題布局。
我們直接在路由插件中使用主題插件中生成的布局?jǐn)?shù)據(jù),代碼很簡(jiǎn)單,根據(jù)前面的layout來(lái)生成一個(gè)布局路由,并添加到返回值中即可,這樣所有parentId
為DocLayout.specifier
的頁(yè)面就能使用該布局了。
// /src/features/routes.ts ... let docLayoutId : undefined | string = undefined; // @ts-ignore const { DocLayout } = api.service.themeData.layouts; // 從舊路由對(duì)象中獲取放入返回值中 if (DocLayout) { docLayoutId = DocLayout.specifier; routes[DocLayout.specifier] = { id: DocLayout.specifier, path: '/', file: DocLayout.source, parentId: undefined, absPath: '/', isLayout: true, }; } ...
使用同步偽代碼來(lái)描述上面流程
// /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)了簡(jiǎn)易的主題插件,運(yùn)行代碼可以看到和上節(jié)運(yùn)行結(jié)果一樣:
路由和頁(yè)面問(wèn)題基本解決了,接下來(lái)就要開(kāi)始正式解析markdown
文件。
以上就是umi插件開(kāi)發(fā)仿dumi項(xiàng)目實(shí)現(xiàn)頁(yè)面布局詳解的詳細(xì)內(nèi)容,更多關(guān)于umi插件仿dumi頁(yè)面布局的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- umi插件開(kāi)發(fā)仿dumi項(xiàng)目自動(dòng)生成導(dǎo)航欄實(shí)現(xiàn)詳解
- umi插件開(kāi)發(fā)仿dumi項(xiàng)目加載markdown文件實(shí)現(xiàn)詳解
- umi插件開(kāi)發(fā)仿dumi項(xiàng)目實(shí)現(xiàn)markdown文件轉(zhuǎn)為頁(yè)面
- 獨(dú)立使用umi的核心插件模塊示例詳解
- Umi4集成阿里低代碼框架lowcode-engine實(shí)現(xiàn)
- umi插件開(kāi)發(fā)仿dumi項(xiàng)目實(shí)現(xiàn)基礎(chǔ)路由解析
相關(guān)文章
本地存儲(chǔ)localStorage設(shè)置過(guò)期時(shí)間示例詳解
這篇文章主要為大家介紹了本地存儲(chǔ)localStorage設(shè)置過(guò)期時(shí)間示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01以JS開(kāi)發(fā)為例詳解版本號(hào)的作用與價(jià)值
這篇文章主要為大家介紹了以JS開(kāi)發(fā)為例詳解版本號(hào)的作用與價(jià)值詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09精確到按鈕級(jí)別前端權(quán)限管理實(shí)現(xiàn)方案
這篇文章主要為大家介紹了精確到按鈕級(jí)別前端權(quán)限管理實(shí)現(xiàn)方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09微信小程序 input輸入框詳解及簡(jiǎn)單實(shí)例
這篇文章主要介紹了微信小程序 input輸入框詳解及簡(jiǎn)單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-01-01JavaScript自動(dòng)化測(cè)試添加頁(yè)面DOM元素唯一ID方案示例
這篇文章主要為大家介紹了JavaScript自動(dòng)化測(cè)試添加頁(yè)面DOM元素唯一ID方案示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09