前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >leader 让我设计实现多标签页~我竟一时没想到好的实现~

leader 让我设计实现多标签页~我竟一时没想到好的实现~

作者头像
若川
发布2022-11-11 16:25:02
8650
发布2022-11-11 16:25:02
举报
文章被收录于专栏:若川视野若川视野

大家好,我是若川。持续组织了近一年的源码共读活动,感兴趣的可以 点此扫码加我微信 ruochuan12 参与,每周大家一起学习200行左右的源码,共同进步。同时极力推荐订阅我写的《学习源码整体架构系列》 包含20余篇源码文章。历史面试系列。另外:目前建有江西|湖南|湖北籍前端群,可加我微信进群。

在平常的后台系统开发中,常常需要缓存一些之前打开的页面,方便操作,多页签就是用来管理页面的,如下图

Umi4.0 版本发布到现在已经有一段时间,全新的框架当然需要学习一下新的特性和功能,由于 Umi4.0 使用了react-router6,所以路由有较大改动,多页签设计也需要重新调整。

文章分为三部分

  1. 设计思路
  2. 遇到的问题
  3. 扩展到自建路由

一、设计思路

之所以要设计多页签,是因为现有的框架路由只能单开,Vue 里面即使有 keep-alive,当面对类似/detail:id这种路由时也只能同时存在一个。

多页签结构如下:路由与组件一对一,组件与实例一对多,实例与页签一对一

以组件实例为维度构建页签,因此需劫持渲染。多页签之所以能劫持渲染是因为其就是一个高阶组件,监听路由变化生成对应的实例。自己维护当前展示的组件列表,从框架提供的全局路由信息生成对应的组件实例,向下渲染。从路由配置里面表现为所有的组件路由都是多页签路由的子路由。

代码语言:javascript
复制
{
  path: '/',
  //多页签对应的组件
  component: '@/layouts/MultiTagRoute.jsx',
  flatMenu: true,
  routes:[
    //以下是实际路由
    {
      name: '组件1',
      path: '/one',
      component: './one',
      icon: 'cluster',
    },
    {
      name: '组件2',
      path: '/two',
      component: './two',
      icon: 'cluster',
    },
  ]
}

那么具体的代码思路如下

  1. 从框架提供的路由信息里面拿到生成组件实例的方法,维护一个页签队列
  2. 监听路由变化,路由不在队列就加入,已经存在就激活,缓存使用 display:'none',因为这里是显示的高阶组件式缓存,可操作性比较高。不是类似 keepalive 那种缓存内置。
  3. 拿到对应的路由信息生成组件实例,向下渲染

二、遇到的问题

react-router6 使用 <Outlet/> 渲染路由,同时还提供了 useOutLet 这个 hooks 获取当前渲染信息。useOutLet 这个 hook 返回的 outlet 比较有意思,它不是传统意义上的类似 children 的 dom 结构。「使用 isValidElement 检测为 true,但是使用 cloneElement 向其传递 props 却不会生效」。因此想要传递 props,只能按照<Outlet context={{}}/>的方式进行参数传递。此外 umi 框架在 prolayout 里面已经使用了<Outlet/>,因此这里只能使用{outlet}这种方式渲染。

umi 还有有一个 useAppData 方法,可以返回组件实例

代码语言:javascript
复制
declare function useAppData(): {
  routes: Record<id, Route>;
  routeComponents: Record<id, Promise<React.ReactComponent>>;
  clientRoutes: ClientRoute[];
  pluginManager: any;
  rootElement: string;
  basename: string;
  clientLoaderData: { [routeKey: string]: any };
  preloadRoute: (to: string) => void;
};

routeComponents 里面包含了每一个组件的工厂函数,可以直接使用<Component>进行渲染。这种方式使用 props 就可以传递参数,可以根据实际情况选择。

监听路由变化使用 useLocation,在多页签里面使用 useEffect 监听 location,此外 location 也能携带一些参数,用于丰富多页签的功能,例如刷新当前页签、跳转前关闭当前页签,跳转后自动刷新等功能,只要在 query 参数里面约定好即可。

三、扩展到自建路由

有了上面的设计思路,平时开发中也会遇到自建路由的需求。路由本质就是路径字符串到组件的映射。我们自建路由的配置也应如下

代码语言:javascript
复制
{
  name: '多页签',
  component: React.lazy(() => import('./multiTag')),
  path: '/',
  children: [
    {
      name: '组件A',
      path: '/componentA',
      component: React.lazy(() => import('./componentA')),
    },
    {
      name: '组件B',
      path: '/componentB',
      component: React.lazy(() => import('./componentB')),
    }
  ]
}

使用 context 维护全局字符串路径,向下传递自建跳转方法就可以模拟路由跳转,自建的路径信息数据结构与 location 保持一致就可以。

这里还有一点需要注意的地方,传统异步加载是这样的方式() => import('./componentA'),没有添加 React.lazy,这样加载出来的文件会被解析为 module。

图片

它不是一个 react 组件,如果使用了 hooks 在某些情况会出现render more than one hooks 的报错,使用时需要注意。

四、总结

以上就是多页签的总体设计思路,大家有什么想法,欢迎在评论区交流。

················· 若川简介 ·················

你好,我是若川,毕业于江西高校。现在是一名前端开发“工程师”。写有《学习源码整体架构系列》20余篇,在知乎、掘金收获超百万阅读。 从2014年起,每年都会写一篇年度总结,已经坚持写了8年,点击查看年度总结。 同时,最近组织了源码共读活动,帮助5000+前端人学会看源码。公众号愿景:帮助5年内前端人走向前列。

扫码加我微信 lxchuan12、拉你进源码共读

今日话题

目前建有江西|湖南|湖北 籍 前端群,想进群的可以加我微信 lxchuan12 进群。分享、收藏、点赞、在看我的文章就是对我最大的支持~

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-10-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 若川视野 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、设计思路
  • 二、遇到的问题
  • 三、扩展到自建路由
  • 四、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档