dojo dragon main logo

出口

出口代表应用程序的视觉位置,根据匹配的路由渲染不同的内容。与使用路由相比,使用出口减少了所需的样板代码,多个路由可以与同一个出口关联,以更自然、更准确地构建应用程序输出。

考虑一个典型的应用程序布局,它包含一个左侧菜单和一个主内容视图,根据路由,它有一个右侧边栏。

-------------------------------------------------------------------
|        |                                            |           |
|        |                                            |           |
|        |                                            |           |
|        |                                            |           |
|        |                                            |           |
|  menu  |                   main                     | side-menu |
|        |                                            |           |
|        |                                            |           |
|        |                                            |           |
|        |                                            |           |
|        |                                            |           |
-------------------------------------------------------------------

下面的路由配置将所有主页面指定到主内容出口,但将widget指定到side-menu出口。这使得能够构建一个应用程序,该应用程序根据路由不断渲染主内容,但也包括widget路由的所有子路由的右侧菜单。

const routes = [
	{
		id: 'landing',
		path: '/',
		outlet: 'main',
		defaultRoute: true
	},
	{
		id: 'widget',
		path: 'widget/{widget}',
		outlet: 'side-menu',
		children: [
			{
				id: 'tests',
				path: 'tests',
				outlet: 'main'
			},
			{
				id: 'overview',
				path: 'overview',
				outlet: 'main'
			},
			{
				id: 'example'
				path: 'example/{example}',
				outlet: 'main'
			}
		]
	}
];

在上面的路由配置中,定义了两个出口,mainside-menu,下面显示了一个使用出口的简化应用程序布局。默认情况下,Outlet将渲染任何与出口匹配的路由 ID 相等的键,在本例中为main。如果向Outlet传递一个函数,则它将在匹配到指定出口的任何路由时渲染。

import { create, tsx } from '@dojo/framework/core/vdom';
import Outlet from '@dojo/framework/routing/Outlet';

import Menu from './Menu';
import SideMenu from './SideMenu';
import Landing from './Landing';
import Tests from './Tests';
import Example from './Example';

const factory = create();

const App = factory(function App() {
	return (
		<div>
			<Menu />
			<main>
				<div>
					<Outlet id="main">
						{{
							landing: <Landing />,
							tests: <Tests />,
							example: ({ params: { example }}) => <Example example={example}/>,
							overview: <Example example="overview"/>
						}}
					</Outlet>
				</div>
				<div>
					<Outlet id="side-menu">
						{({ params: { widget }}) => <SideMenu widget={widget}>}
					</Outlet>
				</div>
			</main>
		</div>
	);
});

App的节点结构看起来很好,简洁地代表了用户实际的视觉输出,并且重复最少,但仍然需要在不同的路由中重复使用Example小部件。这可以通过使用matcher属性来覆盖默认的路由匹配规则来解决。matcher接收defaultMatchesmatchDetailsMap,以便进行自定义匹配决策。在下面的最终示例中,Example的使用已合并到一个新的keydetails,该key不存在作为路由。除非我们覆盖默认匹配以在exampleoverview路由匹配时将其设置为true,否则它永远不会与出口匹配。最后,在details渲染器中,example属性已默认为overview,以保持与以前相同的行为。

import { create, tsx } from '@dojo/framework/core/vdom';
import Outlet from '@dojo/framework/routing/Outlet';

import Menu from './Menu';
import SideMenu from './SideMenu';
import Landing from './Landing';
import Tests from './Tests';
import Example from './Example';

const factory = create();

const App = factory(function App() {
	return (
		<div>
			<Menu />
			<main>
				<div>
					<Outlet id="main" matcher={(defaultMatches, matchDetailsMap) => {
						defaultMatches.details = matchDetailsMap.has('example') || matchDetailsMap.has('overview');
						return defaultMatches;
					}}>
						{{
							landing: <Landing />,
							tests: <Tests />,
							details: ({ params: { example = "overview" }}) => <Example example={example}/>,
						}}
					</Outlet>
				</div>
				<div>
					<Outlet id="side-menu">
						{({ params: { widget }}) => <SideMenu widget={widget}>}
					</Outlet>
				</div>
			</main>
		</div>
	);
});