dojo dragon main logo

介绍

Dojo 的路由包为 Web 应用程序提供了一流的声明式路由解决方案。小部件是 Dojo 应用程序中的基本概念,路由也不例外。Dojo 路由提供了一组小部件,它们直接集成到 Dojo 应用程序中,并使应用程序小部件能够与路由相关联,而不会影响其功能、可重用性或属性接口。

功能 描述
多个历史管理器 路由提供了一组历史管理器,具体取决于应用程序的需求
开箱即用的路由小部件 有一组开箱即用的路由小部件,例如 LinkActiveLink
自动代码拆分 结合 @dojo/cli-build-app,顶级路由会自动进行代码拆分

基本用法

将路由添加到应用程序

  • 添加一个初始路由配置,该配置定义一个映射到路由标识符和 outlet 名称的单个 URL 路径。

src/routes.ts

export default [
	{
		id: 'home',
		path: 'home',
		outlet: 'main'
	},
	{
		id: 'about',
		path: 'about',
		outlet: 'main'
	},
	{
		id: 'profile',
		path: 'profile',
		outlet: 'main'
	}
];
  • 通过将路由器注册到应用程序注册表中,将应用程序配置为路由感知。

src/main.tsx

import renderer, { tsx } from '@dojo/framework/core/vdom';
import Registry from '@dojo/framework/core/Registry';
import { registerRouterInjector } from '@dojo/framework/routing/RouterInjector';

import routes from './routes';
import App from './App';

const registry = new Registry();
// creates a router with the routes and registers the router with the registry
registerRouterInjector(routes, registry);

const r = renderer(() => <App />);
r.mount({ registry });
  • 添加一个 Route 小部件,当访问 home 路由时显示文本“主页”。Route 是一个小部件,当匹配路由 ID 的路径时,它会显示一些内容。应用程序的 src/routes.ts 文件通过 Routeid 属性将路由与 ID 关联起来。

src/App.tsx

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

const factory = create();

export default factory(function App() {
	return (
		<div>
			<Route id="home" renderer={() => <div>Home</div>} />
			<Route id="about" renderer={() => <div>About</div>} />
			<Route id="profile" renderer={() => <div>Profile</div>} />
		</div>
	);
});

或者使用出口和 Outlet 小部件,查看 Outlet 文档 以获取更多信息

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

const factory = create();

export default factory(function App() {
	return (
		<div>
			<Outlet id="main">
				{{
					home: <div>Home</div>,
					about: <div>About</div>,
					profile: <div>Profile</div>
				}}
			</Outlet>
		</div>
	);
});
  • 路由的 URL 由路由配置的 path 元素确定。在本例中,指定了 home,因此可以通过 URL 路径 /#home 访问该路由。
    • 默认情况下,路由器使用 HashHistory 历史管理器,该管理器需要在路由路径之前使用 #。其他 历史管理器 可用于支持其他历史管理机制。

路径和查询参数

路径参数是路由配置中的占位符,将匹配该段的任何值。参数使用花括号定义,例如:{param}

src/routes.ts

export default [
	{
		id: 'home',
		path: 'home/{page}',
		outlet: 'home'
	}
];

参数值将注入到匹配的 Routerenderer 属性中。

src/App.tsx

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

const factory = create();

export default factory(function App() {
	return (
		<div>
			<Route id="home" renderer={(matchDetails) => <div>{`Home ${matchDetails.params.page}`}</div>} />
		</div>
	);
});

查询参数也可以添加到路由 URL 中。与正常的查询参数一样,第一个查询参数必须以 ? 为前缀,其他查询参数以 & 字符分隔。请注意,在使用查询参数时,路由配置不会改变。

src/routes.ts

export default [
	{
		id: 'home',
		path: 'home/{page}?{queryOne}&{queryTwo}',
		outlet: 'home'
	}
];

src/App.tsx

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

const factory = create();

export default factory(function App() {
	return (
		<div>
			<Route
				id="home"
				renderer={(matchDetails) => {
					const { queryParams } = matchDetails;
					return <div>{`Home ${queryParams.queryOne}-${queryParams.queryTwo}`}</div>;
				}}
			/>
		</div>
	);
});

如果浏览器指向 URL 路径 /home/page?queryOne=modern&queryTwo=dojo,则查询参数将作为 MatchDetails 类型的对象注入到匹配的 Routerenderer 方法中,并通过该对象的 queryParams 属性访问。使用此 URL,页面将显示“Hello modern-dojo”。如果未提供查询参数,则其值将设置为 undefined

可选查询参数

路径参数始终是必需的,因为它们是路由路径的一部分,但查询参数有时可能是可选的。要定义可选查询参数,请在查询参数的结束括号之前添加 ?。这指示路由器即使未提供此参数的值也能生成链接。

src/routes.ts

export default [
	{
		id: 'home',
		path: 'home?{page?}',
		outlet: 'home'
	}
];

在本例中,page 现在是可选的,这意味着路由器可以在没有 page 值的情况下生成链接,并且查询参数将不会添加到 URL 中。

默认路由和参数

  • 通过更新路由配置以在首选路由中包含 defaultRoute: true 来指定默认路由。如果未提供任何路由或未注册请求的路由,则默认路由用于在初始加载时重定向应用程序。

src/routes.ts

export default [
	{
		id: 'home',
		path: 'home',
		outlet: 'home',
		defaultRoute: true
	}
];

如果默认路由具有路径或查询参数,则需要指定一个默认值的映射。

src/routes.ts

export default [
	{
		id: 'home',
		path: 'home/{page}',
		outlet: 'home',
		defaultRoute: true,
		defaultParams: {
			page: 'about'
		}
	}
];

通配符路由

* 字符可用于指示通配符路由。路由将正常匹配,直到 *,并且将匹配该点的任何路径。通配符路由永远不会优先于没有通配符的其他匹配路由。* 隐式指示匹配的结束,并且在路由配置中 * 之后指定的任何段都将被忽略。实际 URL 中的任何其他段都将与 matchDetails 中名为 wildcardSegments 的数组属性一起传递。

export default [
	{
		id: 'catchall',
		// Anything after the asterisk will be ignored in this config
		path: '*',
		outlet: 'catchall'
	},
	// This path will be preferred to the wildcard as long as it matches
	{
		id: 'home',
		path: 'home',
		outlet: 'home'
	}
];

* 之后和包括匹配的 * 在内的所有段都将作为 wildcardSegments 注入到匹配的 Routerenderer 属性中。

src/App.tsx

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

const factory = create();

export default factory(function App() {
	return (
		<div>
			<Route id="home" renderer={(matchDetails) => <div>{`Home ${matchDetails.params.page}`}</div>} />
			<Route
				id="catchall"
				renderer={(matchDetails) => <div>{`Matched Route ${matchDetails.wildcardSegments.join(', ')}`}</div>}
			/>
		</div>
	);
});

Link 小部件是锚标记的包装器,它使使用者能够指定路由 id 来创建指向该路由的链接。如果生成的链接需要路由中没有的特定路径或查询参数,则可以通过 params 属性传递它们。

链接属性

  • to: string: route ID。
  • params: { [index: string]: string }: 用于生成链接的路由参数。
  • onClick: (event: MouseEvent) => void(可选):单击 Link 时调用的函数。

除了 Link 特定属性之外,所有标准 VNodeProperties 都可用于 Link 小部件,因为它们会创建锚标记。

src/App.tsx

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

const factory = create();

export default factory(function App() {
	return (
		<div>
			<Link to="home" params={{ foo: 'bar' }}>
				Link Text
			</Link>
		</div>
	);
});

ActiveLink 小部件是 Link 小部件的包装器,如果链接当前处于活动状态,它会在 a 节点上条件地设置类。

ActiveLink 属性

  • activeClasses: string[]: 当匹配 Link 的路由时要应用的类数组。
import { create, tsx } from '@dojo/framework/core/vdom';
import { ActiveLink } from '@dojo/framework/routing/ActiveLink';

const factory = create();

export default factory(function App() {
	return (
		<div>
			<ActiveLink to="home" params={{ foo: 'bar' }} activeClasses={['link-active']}>
				Link Text
			</ActiveLink>
		</div>
	);
});

按路由代码拆分

当使用 @dojo/cli-build-app 时,Dojo 默认情况下支持对所有顶级路由进行自动代码拆分。这意味着 Routerenderer 中引用的所有小部件都将包含该路由的特定捆绑包,该捆绑包将在用户访问该路由时延迟加载。

要利用代码拆分,有 4 个规则

  1. 路由配置需要是 src/routes.ts 模块中的默认导出。
  2. 小部件必须是其模块的默认导出。
  3. renderer 属性必须内联定义。
  4. 路由配置中的 idoutlet 必须是静态的,并且内联定义。

src/routes.ts

export default [
	{
		id: 'home',
		path: 'home',
		outlet: 'home'
	},
	{
		id: 'about',
		path: 'about',
		outlet: 'about',
		children: [
			{
				id: 'company',
				path: 'company',
				outlet: 'about-company'
			}
		]
	},
	{
		id: 'profile',
		path: 'profile',
		outlet: 'profile'
	},
	{
		id: 'settings',
		path: 'settings',
		outlet: 'settings'
	}
];

使用上面的路由配置,以下示例将为 Route 的渲染器中返回的每个小部件生成 4 个单独的捆绑包,HomeAboutProfileSettings

src/App.tsx

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

import Home from './Home';
import About from './About';
import Profile from './Profile';
import Settings from './Settings';

const factory = create();

export default factory(function App() {
	return (
		<div>
			<Route id="home" renderer={() => <Home />} />
			<Route id="about" renderer={() => <About />} />
			<Route id="profile" renderer={() => <Profile />} />
			<Route id="settings" renderer={() => <Settings />} />
		</div>
	);
});