Outlet
outlet 用于标注应用程序中的显示位置,在这个位置可以根据匹配到的路由渲染不同的内容。与使用路由相比,使用 outlet 能减少编写样板代码,将多个路由关联到同一个 outlet 下,以便更自然、更准确的描述应用程序的输出结构。
考虑一个经典的应用程序布局,其中包括一个左侧菜单和主内容视图,在主内容视图右侧有一个根据路由变化的菜单栏:
-------------------------------------------------------------------
| | | |
| | | |
| | | |
| | | |
| | | |
| menu | main | side-menu |
| | | |
| | | |
| | | |
| | | |
| | | |
-------------------------------------------------------------------
以下的路由配置将所有的主页面指向显示主内容的 outlet,将 widget
指向显示 side-menu
的 outlet。便可以构建出这样一个应用程序,它始终根据路由动态渲染主内容区,也会根据右侧菜单栏中 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'
}
]
}
];
在上面的路由配置中,定义了两个 outlet,main
和 side-menu
,下面显示了使用这两个 outlet 的简化版应用程序布局。默认情况下,Outlet
将会渲染 key 值与 outlet 下的路由 id 匹配的路由,如本例中的 main
。如果为 Outlet
传入的是一个函数,则与 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
会接收 defaultMatches
和 matchDetailsMap
两个参数,以便自定义匹配策略。在下面最后一个示例中,将重复使用的 Example
合并为一个新 key
,即 details
,但在路由定义中并不存在。如果不覆写默认匹配,让匹配到 example
或 overview
路由时将其设置为 true,则此 outlet 永远不会匹配到 details
。最后,在 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>
);
});