Dojo 路由
路由
commit b682b06ace25eea86d190e56dd81042565b35ed1
Dojo 應用程式的路由
Features
部件(Widget)是 Dojo 應用程式的基本概念,因此 Dojo 路由提供了一組與應用程式中的部件直接整合的元件。這些元件能將部件註冊到路由上,且不需要掌握路由相關的任何知識。Dojo 應用程式中的路由包括以下內容:
-
Outlet
部件封裝器指定 route 的 outlet key 和表現檢視之間的對映關係 -
Route
配置,用於在路徑和 outlet key 之間建立對映關係 -
Router
基於當前路徑解析Route
-
History
提供器負責向Router
通知路徑的更改 -
Registry
將Router
注入到部件系統中
Route 配置
RouteConfig
用於註冊應用程式的路由,它定義了路由的path
、關聯的outlet
以及巢狀的子RouteConfig
。一個完整的路由就是由遞迴巢狀的 Route 構成的。
路由配置示例:
import { RouteConfig } from '@dojo/framework/routing/interfaces'; const config: RouteConfig[] = [ { path: 'foo', outlet: 'root', children: [ { path: 'bar', outlet: 'bar' }, { path: 'baz', outlet: 'baz', children: [ { path: 'qux', outlet: 'qux' } ] } ] } ];
此配置將註冊以下路由和 outlet:
Route | Outlet |
---|---|
/foo
|
root
|
/foo/bar
|
bar
|
/foo/baz
|
baz
|
/foo/baz/qux
|
qux
|
路徑引數
在RouteConfig
的 path 屬性中,在path
的值中使用大括號可定義路徑引數。 Parameters will match any segment and the value of that segment is made available to matching outlets via theOutlet
options. The parameters provided to child outlets will include any parameters from matching parent routes.
const config = [ { path: 'foo/{foo}', outlet: 'foo' } ];
具有路徑引數的路由,可為每個路由指定預設的引數。當用沒有指定引數的 outlet 生成一個連結,或者當前路由中不存在引數時,可使用這些預設引數值。
const config = [ { path: 'foo/{foo}', outlet: 'foo', defaultParams: { foo: 'bar' } } ];
可使用可選的配屬屬性defaultRoute
來設定預設路由,如果當前路由沒有匹配到已註冊的路由,就使用此路由。
const config = [ { path: 'foo/{foo}', outlet: 'foo', defaultRoute: true } ];
Router
Router
用於註冊,將 route 配置資訊傳入 Router 的建構函式即可:
const router = new Router(config);
會自動為 router 註冊一個HashHistory
歷史管理器(history manager)。可在第二個引數中傳入其他歷史管理器。
import { MemoryHistory } from '@dojo/framework/routing/MemoryHistory'; const router = new Router(config, { HistoryManager: MemoryHistory });
使用應用程式路由配置建立路由後,需要讓應用程式中的所有元件可使用這些路由。這是通過使用@dojo/framework/widget-core/Registry
中的Registry
,定義一個將invalidator
連線到 router 的nav
事件的注入器,並返回router
例項實現的。這裡使用 key 來定義注入器,路由器的預設 key 值為router
。
import { Registry } from '@dojo/framework/widget-core/Registry'; import { Injector } from '@dojo/framework/widget-core/Injector'; const registry = new Registry(); // 假設我們有一個可用的 router 例項 registry.defineInjector('router', () => { router.on('nav', () => invalidator()); return () => router; };
注意:路由提供了註冊 router 的快捷方法。
最後,為了讓應用程式中的所有部件都能使用registry
,需要將其傳給 vdomrenderer
的.mount()
方法。
const r = renderer(() => v(App, {})); r.mount({ registry });
History Managers
路由自帶三個歷史管理器,用於監視和更改導航狀態:HashHistory
、StateHistory
和MemoryHistory
。預設使用HashHistory
,但是可在建立Router
時傳入不同的HistoryManager
。
const router = new Router(config, { HistoryManager: MemoryHistory });
Hash History
基於雜湊的管理器使用片段識別符號(fragment identifier)來儲存導航狀態,是@dojo/framework/routing
中的預設管理器。
import { Router } from '@dojo/framework/routing/Router'; import { HashHistory } from '@dojo/framework/routing/history/HashHistory'; const router = new Router(config, { HistoryManager: HashHistory });
歷史管理器有current
、set(path: string)
和prefix(path: string)
三個API。HashHistory
類假定全域性物件是瀏覽器的window
物件,但可以顯式提供物件。管理器使用window.location.hash
和hashchange
事件的事件監聽器。current
訪問器返回當前路徑,不帶 # 字首。
State History
基於狀態的歷史管理器使用瀏覽器的 history API:pushState()
和replaceState()
,來新增和修改歷史紀錄。狀態歷史管理器需要伺服器端支援才能有效工作。
Memory History
MemoryHistory
不依賴任何瀏覽器 API,而是保持其自身的內部路徑狀態。不要在生產應用程式中使用它,但在測試路由時卻很有用。
import { Router } from '@dojo/framework/routing/Router'; import { MemoryHistory } from '@dojo/framework/routing/history/MemoryHistory'; const router = new Router(config, { HistoryManager: MemoryHistory });
Outlet Event
當每次進入或離開 outlet 時,都會觸發router
例項的outlet
事件。outlet 上下文、enter
或exit
操作都會傳給事件處理函式。
router.on('outlet', ({ outlet, action }) => { if (action === 'enter') { if (outlet.id === 'my-outlet') { // do something, perhaps fetch data or set state } } });
Router Context Injection
RouterInjector
模組匯出一個幫助函式registerRouterInjector
,它組合了Router
例項的例項化,註冊 router 配置資訊和為提供的 registry 定義注入器,然後返回router
例項。
import { Registry } from '@dojo/framework/widget-core/Registry'; import { registerRouterInjector } from '@dojo/framework/routing/RoutingInjector'; const registry = new Registry(); const router = registerRouterInjector(config, registry);
可使用RouterInjectiorOptions
覆蓋預設值:
import { Registry } from '@dojo/framework/widget-core/Registry'; import { registerRouterInjector } from '@dojo/framework/routing/RoutingInjector'; import { MemoryHistory } from './history/MemoryHistory'; const registry = new Registry(); const history = new MemoryHistory(); const router = registerRouterInjector(config, registry, { history, key: 'custom-router-key' });
Outlets
路由整合的一個基本概念是outlet
,它是與註冊的應用程式 route 關聯的唯一識別符號。Outlet
是一個標準的 dojo 部件,可在應用程式的任何地方使用。Outlet
部件有一個精簡的 API:
-
id
: 匹配時執行renderer
的 outlet 標識。 -
renderer
: 當 outlet 匹配時呼叫的渲染函式。 -
routerKey
(可選): 在 registry 中定義路由時使用的key
- 預設為router
。
接收渲染的 outlet 名稱和一個renderer
函式,當 outlet 匹配時,該函式返回要渲染的DNode
。
render() { return v('div', [ w(Outlet, { id: 'my-outlet', renderer: () => { return w(MyWidget, {}); }}) ]) }
可為renderer
函式傳入MatchDetails
引數,該引數提供路由專有資訊,用於確定要渲染的內容和計算傳入部件的屬性值。
interface MatchDetails { /** * Query params from the matching route for the outlet */ queryParams: Params; /** * Params from the matching route for the outlet */ params: Params; /** * Match type of the route for the outlet, either `index`, `partial` or `error` */ type: MatchType; /** * The router instance */ router: RouterInterface; /** * Function returns true if the outlet match was an `error` type */ isError(): boolean; /** * Function returns true if the outlet match was an `index` type */ isExact(): boolean; }
render() { return v('div', [ w(Outlet, { id: 'my-outlet', renderer: (matchDetails: MatchDetails) => { if (matchDetails.isError()) { return w(ErrorWidget, {}); } if (matchDetails.isExact()) { return w(IndexWidget, { id: matchDetails.params.id }); } return w(OtherWidget, { id: matchDetails.params.id }); }}) ]) }
Global Error Outlet
只要註冊了一個error
匹配型別,就會自動為匹配的 outlet 新增一個全域性 outlet,名為errorOutlet
。這個 outlet 用於為任何未知的路由渲染一個部件。
render() { return w(Outlet, { id: 'errorOutlet', renderer: (matchDetails: MatchDetails) => { return w(ErrorWidget, properties); } }); }
Link
Link
元件是對 DOM 節點a
的封裝,允許使用者為建立的連結指定一個outlet
。也可以通過將isOutlet
屬性值設定為false
來使用靜態路由。
如果生成的連結需要指定在 route 中不存在的路徑或查詢引數,可使用params
屬性傳入。
import { Link } from '@dojo/framework/routing/Link'; render() { return v('div', [ w(Link, { to: 'foo', params: { foo: 'bar' }}, [ 'Link Text' ]), w(Link, { to: '#/static-route', isOutlet: false, [ 'Other Link Text' ]) ]); }
所有標準的VNodeProperties
都可用於Link
元件,因為最終是使用@dojo/framework/widget-core
中的v()
來建立一個a
元素。
ActiveLink
ActiveLink
元件是對Link
元件的封裝,如果連結處於啟用狀態,則可以為a
節點設定樣式類。
import { ActiveLink } from '@dojo/framework/routing/ActiveLink'; render() { return v('div', [ w(ActiveLink, { to: 'foo', params: { foo: 'bar' }, activeClasses: [ 'link-active' ]}, [ 'Link Text' ]) ]); }