通常のバックグラウンド システム開発では、操作を簡単にするために、以前に開いたページをキャッシュする必要があることがよくあります。次の図に示すように、複数のタブを使用してページを管理します。
Umi4.0のバージョンがリリースされてからしばらく経ちました. もちろん新しいフレームワークは新しい特徴や機能を学ぶ必要があります. Umi4.0を使用して以来react-router6
, ルーティングは大きく変更されました. マルチタブのデザインも必要です.再調整する。
記事は3つのパートに分かれています
- デザインのアイデア
- 発生した問題
- 自作ルーティングに拡張
1. 設計思想
複数のタブを設計する理由は、既存のフレームワーク ルートは個別にしか開くことができないためです. Vue にキープアライブがあっても、/detail:id
そのようなルートに直面した場合、同時に存在できるのは 1 つだけです.
マルチタブ構造は次のとおりです: 1 対 1 のルートとコンポーネント、1 対多のコンポーネントとインスタンス、1 対 1 のインスタンスとタブ
タブはコンポーネント インスタンスをディメンションとして構築されるため、レンダリングをハイジャックする必要があります。複数のタブがレンダリングをハイジャックできる理由は、ルーティングの変更をリッスンして対応するインスタンスを生成する上位コンポーネントであるためです。現在表示されているコンポーネントのリストを自分で維持し、フレームワークによって提供されるグローバル ルーティング情報から対応するコンポーネント インスタンスを生成し、下方向にレンダリングします。ルーティング構成から、すべてのコンポーネント ルートはマルチタブ ルーティングのサブルートです。
{
path: '/',
//多页签对应的组件
component: '@/layouts/MultiTagRoute.jsx',
flatMenu: true,
routes:[
//以下是实际路由
{
name: '组件1',
path: '/one',
component: './one',
icon: 'cluster',
},
{
name: '组件2',
path: '/two',
component: './two',
icon: 'cluster',
},
]
}
复制代码
次に、特定のコードのアイデアは次のとおりです
- フレームワークが提供するルーティング情報からコンポーネントインスタンスを生成するメソッドを取得し、タブキューを維持する
- ルートの変更をリッスンし、ルートがキューにない場合はルートに参加し、すでに存在する場合はアクティブ化し、キャッシュに display:'none' を使用します。これは、ここに表示される高レベルのコンポーネント キャッシュであり、より操作しやすいためです。 . キープアライブのような組み込みキャッシュではありません。
- 対応するルーティング情報を取得して、コンポーネント インスタンスを生成し、下方向にレンダリングします
第二に、遭遇した問題
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。
反応コンポーネントではないので、フックを使うとrender more than one hooks
エラーになる場合があるので注意が必要です。
4. まとめ
上記はマルチタブの一般的なデザイン案です. アイデアがあれば, コメント欄での交換を歓迎します.