020 Umi@4 中如何实现动态菜单

简介: 020 Umi@4 中如何实现动态菜单

image.png

在数据流章节中我有一段这样的描述:

为什么都不是最佳实践了,我还要一直提 dva


因为纯 hooks 的数据流方案,存在天然的局限性,因为 react hook 只能用在 react 的上下文环境中,但是在 Umi 中我们还有一些环节是不在 react 的上下文的,比如如果我们要前置判断用户登录情况,或者提前获取用户可访问菜单数据,或者其他的一些项目前置数据,我们都需要“上升”我们的数据流方案。


其实这一段我是想描述在 Umi 项目中还存在不在 React 生命周期中的数据流管理时机,所以我们依旧需要 dva 来管理我们的项目数据,其实最主要的点还是 dva 是一个很流行的数据流管理方案,在我们的项目中有很长的使用情况,团队内对它都比较熟悉,因此就算出现了其他可替代的方案,我们依旧会选择使用较为“古老”的方案。


西门吹风凉飕飕 提到的疑惑,其实在第 10 课使用 Umi 配置,定制化你自己的 Umi 框架中,我们讲解 Umi 中的运行时配置 - render 时,就已经演示过代码了。那时候,我们还没讲解到数据流和请求这些,今天我们就将这几个方案串联起来。



升级插件

@alita/plugins 升级到 3.0.3,因为我们添加了一个获取 Dva app 的 Api ,这使你能够在任何的 js 环境中继续使用 Dva

"@alita/plugins": "3.0.3",
复制代码

`@alita/plugins` 和 `@umijs/plugins` 中的 dva 插件有什么差别吗?

其实这两个插件现在的功能是一致的,alita 中的 Dva 插件就是从 umijs 中复制出来的,唯一的不同是,alita 中的插件,添加了约定的 Dva module 类型定义。可以更加规范的在 Typescript 中使用 Dva。



Mock 数据

在第 16 课 Umi 项目中的菜单与权限 中,我们讲解了 Umi 项目中的菜单与权限,我们使用了unaccessible 数组来管理我们的菜单,所以我们想将它转移到本地的“服务端”。

新建 Mock 文件 mock/accessible.ts

export default {
  "POST /api/rule": {
    success: true,
    data: ["/hooks", "/useEffect", "/usemodel", "/useState"],
  },
};
复制代码

如果你不知道这有什么用,请阅读第 18 课 Umi 中使用 mockjs 完善前后端分离



增加配置

import { defineConfig } from "umi";
export default defineConfig({
  plugins: [
    // 其他插件不用删除,这里只是简略展示
    require.resolve("@alita/plugins/dist/dva"),
  ],
  // 其他配置不用删除,这里只是简略展示
  dva: {
    enableModelsReExport: {},
  },
});
复制代码


enableModelsReExport 配置就是 alita 中的 Dva 插件特有的,会将 module 文件中的 State 类型导出,这有个要求,每个 module 必须写明 State 的类型,不然程序就会报错。通过约定,我们可以很方便的解决问题。

当然了如果你觉得这个功能你不需要,你可以不开启这个配置,或者直接使用 umijs/plugins 中的 Dva 插件。


api.config.dva?.enableModelsReExport
    ? models
        .map((model: { file: string; namespace: string }) => {
          const { file, namespace } = model;
          // prettier-ignore
          // export type { IndexModelState } from '/Users/xiaohuoni/next-alita-app/src/models/index';
          return `export type { ${namespace.replace(/( |^)[a-z]/g, (L) => L.toUpperCase())}ModelState } from '${winPath(file.replace(extname(file), ''))}';`;
        })
        .join('\r\n')
    : ''
复制代码



添加 Dva module 文件

新建 Dva module 文件 src/models/global.ts

import { Reducer } from "umi";
export interface GlobalModelState {
  unaccessible: string[];
}
export interface GlobalModelType {
  namespace: "global";
  state: GlobalModelState;
  reducers: {
    save: Reducer<GlobalModelState>;
  };
}
const GlobalModel: GlobalModelType = {
  namespace: "global",
  state: {
    unaccessible: [],
  },
  reducers: {
    save(state, action) {
      return {
        ...state,
        ...action.payload,
      };
    },
  },
};
export default GlobalModel;
复制代码

注意以上内容必须的是 GlobalModel 对象,剩余部分都是为了更好的用类型去定义和规范 GlobalModel 对象。



在 render 中发起请求

在运行时配置中 src/app.tsrender 中发起请求,如果你不知道 render 是啥,请翻阅第 10 课使用 Umi 配置,定制化你自己的 Umi 框架

import { request, getDvaApp } from "umi";
export function render(oldRender: any) {
  request("/api/accessible").then(({ data }) => {
    const app = getDvaApp();
    app?._store.dispatch({
      type: "global/save",
      payload: { unaccessible: data },
    });
  });
  oldRender();
}
复制代码

这里我们通过 getDvaApp 获取到当前项目中的 Dva app,然后使用 _store 上的 dispatch 发起一个 action 将数据更新到 global modules 中。



将 module 数据绑定到页面上

将 global 的数据,绑定到全局布局上, src/layouts/index.tsx

import { connect } from "umi";
import type { ConnectProps, GlobalModelState } from "umi";
interface AppProps extends ConnectProps {
  global: GlobalModelState;
}
const App: React.FC<AppProps> = ({ global }) => {
    const { unaccessible } = global;
    return (<></>)
}
export default connect(({ global }: { global: GlobalModelState }) => ({
  global,
}))(App);
复制代码

以上操作就将页面和 module 进行了双向绑定,只要 global 数据发生变化,就会促使页面进行重绘。

我们只需要取出 global 中的 unaccessible 代替原来“写死”的 unaccessible 即可。



总结

以上操作,看起来比较繁琐,但是如果你对各个概念都有了一定了解,那阅读起来就会很轻松,觉得逻辑非常的清晰。如果你有任何疑问,可以去看看前面的课程,也可以在评论区和我互动。

你应该可以从我的行文内容看出来,我是没有任何“存稿”的,跟这个系列文章,有点类似半直播的方式。我觉得这比我自己“埋头苦干”,要有趣的多,也希望你会喜欢。



目录
相关文章
|
2月前
|
存储 JavaScript UED
在 Vue 组件中动态切换全局样式
【10月更文挑战第5天】
53 1
|
4月前
|
前端开发
如何在前端项目中单独引入 ElementUI 图标以及使用
这篇文章介绍了如何在前端项目中单独引入Element UI的图标文件以及如何使用这些图标。
399 0
如何在前端项目中单独引入 ElementUI 图标以及使用
|
4月前
|
JavaScript 前端开发
【Vue 3】如何实现动态表单生成器的拖拽功能?
【Vue 3】如何实现动态表单生成器的拖拽功能?
|
缓存 前端开发 NoSQL
vue-element-admin实战 | 第二篇: 最小改动接入后台实现根据权限动态加载菜单
vue-element-admin实战 | 第二篇: 最小改动接入后台实现根据权限动态加载菜单
|
JavaScript 前端开发 容器
js如何实现选项卡功能
js如何实现选项卡功能
109 0
umi如何实现路由的动画跳转?
在src下新建一个layouts的文件夹,在layouts文件夹下新建两个文件
156 0
umi如何实现路由的动画跳转?
|
JavaScript 前端开发 UED
Vue中的动态DOM加载:实现更灵活的前端界面
Vue.js是一个流行的JavaScript框架,提供了强大的工具来构建交互式和动态的前端界面。在本博客中,我们将深入探讨Vue中动态加载DOM的方法和实例,以帮助您创建更灵活、响应式的用户界面。
450 0
|
网络架构
基于 vue-antd-admin 开发项目后的几点感想(二):异步路由和菜单
基于 vue-antd-admin 开发项目后的几点感想(二):异步路由和菜单
149 0
|
前端开发
前端学习笔记202304学习笔记第十八天-vue3.0-导航守卫-全局控制路由的访问权限1
前端学习笔记202304学习笔记第十八天-vue3.0-导航守卫-全局控制路由的访问权限1
67 0
|
存储 JavaScript 前端开发
Vue组件滚动加载、懒加载功能的实现,无限滚动加载组件实例演示
Vue组件滚动加载、懒加载功能的实现,无限滚动加载组件实例演示
426 0