广告位联系
返回顶部
分享到

umi插件开发仿dumi项目实现页面布局

JavaScript 来源:互联网 作者:佚名 发布时间:2023-01-30 09:48:12 人浏览
摘要

上一章我们已经完成/docs目录下文件自动生路由功能,本章我们将在此基础上,实现自动生成页面导航的功能。 使用默认模板提供的layout展示路由切换 使用自定义主题插件 使用默认项

上一章我们已经完成/docs目录下文件自动生路由功能,本章我们将在此基础上,实现自动生成页面导航的功能。

  • 使用默认模板提供的layout展示路由切换
  • 使用自定义主题插件

使用默认项目提供的layout文件

在我们创建默认umi项目后,会在/src/layouts下生成一个布局文件:

同时在上一章节我们打印modifyRouteshook的入参,可以看到umi会将该文件转成一个layout router对象,如图所示:

因此我们可以直接将这个layout的id属性,赋值到自动生成的路由的parentId属性上,并添加该对象到返回值中:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

// /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),

    });

    // 默认提供的布局layout的Id

    let docLayoutId : undefined | string = '@@/global-layout';

    // 从旧路由对象中获取放入返回值中

    routes[docLayoutId] = oRoutes[docLayoutId]

    Object.entries(dirRoutes).forEach(([key, route]) => {

      // 这里将文件的路径改为绝对路径,否则umi会默认找/src/pages下组件

      route.file = path.resolve(docDir, route.file);

      // 给页面对象赋值布局Id

      route.parentId = docLayoutId

      routes[route.id] = route;

    });

    return routes;

  });

};

同时我们修改布局文件,将导航改成我们的测试页面路由:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

// /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>

  );

}

运行项目可以看到布局文件已添加到页面中,并可以切换路由:

自定义主题

上面我们通过最简单的方式使用了默认提供的布局文件,这种方式对页面局限性比较大,由于各个项目对页面的展示要求不一样,dumi提供了主题插件来灵活扩展用户自定义布局。同时提供了默认主题,用户可以选择性覆盖默认样式。

本节我们将实现其中的默认主题加载

准备工作

创建主题插件,并注册到插件配置中

1

mkdir /src/features/theme.ts

1

2

3

4

5

6

7

import { defineConfig } from "umi";

export default defineConfig({

    plugins: [

        './src/features/routes.ts',

        './src/features/theme.ts',

    ],

});

创建默认主题目录,将/src/layouts/index.tsx文件复制到这里

1

2

3

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主题插件主要提供的功能有:

  • 加载默认主题布局文件
  • 加载国际化语言包
  • 合并默认主题及自定义主题
  • ...等其他与页面相关功能

本章我们将实现默认布局的加载,并配置到路由中。

modifyAppData

umi提供modifyAppData钩子,用于初始收集应用数据,在dumi中使用这个钩子来初始化主题数据。

我们在主题插件中提供的主题数据后续会被用在修改路由中,即在modifyRoutes阶段使用。因为modifyRoutes是在appData插件的modifyAppData阶段中执行,所以通过before: 'appData让主题插件的modifyAppData在appData插件的modifyAppData之前先初始化完成,这样在modifyRoutes中就可以使用到主题数据。

插件代码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

// /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;

    }, {});

}

另一个比较不优雅的地方是这里使用了api.service来存储生成的主题数据,同样因为上面提到的阶段问题,modifyRoutes是在modifyAppData中执行,所以这里只能用全局变量来存储,否则在修改路由阶段拿不到这里的数据。

执行完成后,api.service.themeData就得到了主题相关的数据:

1

2

3

4

5

6

7

8

9

10

{

  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作为布局模板,现在我们可以将它改成动态添加主题布局。

我们直接在路由插件中使用主题插件中生成的布局数据,代码很简单,根据前面的layout来生成一个布局路由,并添加到返回值中即可,这样所有parentId为DocLayout.specifier的页面就能使用该布局了。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

// /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,

  };

}

...

使用同步伪代码来描述上面流程

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

// /umi/packages/core/src/service/service.ts 中代码

service.collectAppData() {

    // /src/features/theme.ts中代码

    // 配置before: 'appData' 使其先于appData.modifyAppData执行

    themePlugin.modifyAppData() {

        // 这里加载主题数据

        api.service.themeData = loadTheme(DEFAULT_THEME_PATH);

    }

    // /umi/packages/preset-umi/src/features/appData/appData.ts 中代码

    appDataPlugin.modifyAppData() {

        // /src/features/routes.ts中代码

        routesPlugin.modifyRoutes() {

            // 这里使用主题数据生成布局路由

            const { DocLayout } = api.service.themeData.layouts;

            routes[DocLayout.specifier] = {

                ...

                isLayout: true,

            };

        }

    }

}

运行检查

至此我们已经完成生成默认布局,实现了简易的主题插件,运行代码可以看到和上节运行结果一样:

路由和页面问题基本解决了,接下来就要开始正式解析markdown文件。


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 : https://juejin.cn/post/7191468709847695397
相关文章
  • umi插件开发仿dumi项目自动生成导航栏实现
    前面我们已经完成了页面布局和页面路由,现在我们的导航栏还是自己写死在代码中,现在我们来改造成自动根据页面路由来生成导航栏。
  • umi插件开发仿dumi项目实现页面布局

    umi插件开发仿dumi项目实现页面布局
    上一章我们已经完成/docs目录下文件自动生路由功能,本章我们将在此基础上,实现自动生成页面导航的功能。 使用默认模板提供的layout展
  • umi插件开发仿dumi项目实现基础路由解析

    umi插件开发仿dumi项目实现基础路由解析
    umi默认约定在/src/pages添加的(j|t)sx?文件会自动加载为路由。同样我们希望实现在某个目录下添加的markdown文件自动加载成为路由直接访问,本
  • js实现兔年转圈圈动画的代码

    js实现兔年转圈圈动画的代码
    兔年到了,兔年大吉祥,为了庆祝这份喜庆的兔年,今天我们设计一个兔子转圈圈的动画模拟吧。希望大家可以得到我的祝福和好运哦。。
  • Angular8升级至Angular13遇到的问题解决

    Angular8升级至Angular13遇到的问题解决
    根据项目需求,需要把Angular版本从8升级到13,无法从8直接升至13,需要一级一级的升级,本文介绍了在升级Angular版本的时候的一种报错和解
  • Vue3跨域解决方案实例介绍
    vue项目配置代理 vue.config.js 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 const { defineConfig } = require(@vue/cli-service) module.exports = defineConfig({ transpileDependencies:
  • Node.js参数max-old-space-size的介绍
    前言 Old space是 V8 托管(也称为垃圾收集)堆(即 JavaScript 对象所在的位置)中最大和最可配置的部分,而--max-old-space-size标志控制其最大大
  • 用js实现一个网页版节拍器

    用js实现一个网页版节拍器
    平时练尤克里里经常用到节拍器,突发奇想自己用js开发一个。 最后实现的效果如下:ahao430.github.io/metronome/。 代码见github仓库:github.com/
  • js日期格式化yyyy-MM-dd问题

    js日期格式化yyyy-MM-dd问题
    js日期格式化yyyy-MM-dd 方法一 1 2 3 4 5 6 7 8 9 10 11 12 13 function formatDate(date) { console.log(date); // date = new Date(); date = new Date(Date.parse(date.replace(/-/g
  • Nodejs如何解决跨域(CORS)
    Nodejs解决跨域(CORS) 前后端分离的大环境下,受制于同源策略,我们需要懂得实现CORS(Cross-Origin Resource Sharing) 手动配置 在nodejs中,req 和
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计