Umi4.0 multi-tab design

In the normal background system development, it is often necessary to cache some previously opened pages for easy operation. Multiple tabs are used to manage pages, as shown in the following figurepicture

It has been a while since the Umi4.0 version was released. Of course, the new framework needs to learn new features and functions. Since Umi4.0 is used react-router6, the routing has been greatly changed, and the multi-tab design also needs to be readjusted.

The article is divided into three parts

  1. Design ideas
  2. problems encountered
  3. Extend to self-built routing

1. Design ideas

The reason for designing multiple tabs is that the existing framework routes can only be opened individually. Even if there is a keep-alive in Vue, when faced with /detail:idsuch a route, only one can exist at the same time.

The multi-tab structure is as follows: One-to-one route and component, one-to-many component and instance, one-to-one instance and tabpicture

The tab is constructed with the component instance as the dimension, so the rendering needs to be hijacked. The reason why multiple tabs can hijack rendering is because it is a high-order component that listens to routing changes to generate corresponding instances. Maintain the list of currently displayed components by yourself, generate corresponding component instances from the global routing information provided by the framework, and render downwards. From the routing configuration, all component routes are sub-routes of multi-tab routing.

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

Then the specific code idea is as follows

  1. Get the method of generating component instances from the routing information provided by the framework, and maintain a tab queue
  2. Listen for route changes, join the route when it is not in the queue, activate it if it already exists, and use display:'none' for the cache, because it is a high-level component cache displayed here, which is more maneuverable. It is not a built-in cache like keepalive.
  3. Get the corresponding routing information to generate a component instance and render it downward

Second, the problems encountered

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

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

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参数里面约定好即可。

三、扩展到自建路由

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

{
  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。

picture

It is not a react component. If you use hooks, there will be render more than one hooksan error in some cases, you need to pay attention when using it.

4. Summary

The above is the general design idea of ​​multi-tab. If you have any ideas, welcome to exchange in the comment area.

Guess you like

Origin juejin.im/post/7147752724262551565