路由、导航和 URL
Kibana 平台提供了一组工具来帮助开发人员围绕路由和浏览器导航构建一致的体验。其中一些工具位于 core
内部,另一些工具则作为各种插件的一部分提供。
本指南的目的是对可用工具进行高层次的概述,并解释处理路由和浏览器导航的常用方法。
本指南涵盖以下主题
假设您想从您的应用链接到 Discover。构建此类 URL 时,需要考虑两件事
- 前置正确的
basePath
。 - 指定 Discover 状态。
要前置 Kibana 的 basePath
,请使用 core.http.basePath.prepend 助手
const discoverUrl = core.http.basePath.prepend(`/discover`);
console.log(discoverUrl); // http://localhost:5601/bpr/s/space/app/discover
将 Kibana 应用 URL 视为应用插件合同的一部分
- 避免在您的应用代码中硬编码其他应用的 URL。
- 避免生成其他应用的状态并将其序列化为 URL 查询参数。
// Avoid relying on other app's state structure in your app's code:
const discoverUrlWithSomeState = core.http.basePath.prepend(`/discover#/?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:'2020-09-10T11:39:50.203Z',to:'2020-09-10T11:40:20.249Z'))&_a=(columns:!(_source),filters:!(),index:'90943e30-9a47-11e8-b64d-95841ca0b247',interval:auto,query:(language:kuery,query:''),sort:!())`);
相反,每个应用都应该公开一个 locator。其他应用应该使用这些 locator 进行导航或 URL 创建。
// Properly generated URL to *Discover* app. Locator code is owned by *Discover* app and available on *Discover*'s plugin contract.
const discoverUrl = await plugins.discover.locator.getUrl({filters, timeRange});
// or directly execute navigation
await plugins.discover.locator.navigate({filters, timeRange});
为了更好地理解,请查看 Discover locator 的 实现。它允许指定各种 Discover 应用状态片段,例如:索引模式、过滤器、查询、时间范围等等。
有两种方法可以访问其他应用的 locator
- 从目标应用的插件合同中 (首选)。
- 使用
share
插件中的 locator 客户端(如果无法显式插件依赖关系)。
如果您希望其他应用链接到您的应用,那么您应该创建一个 locator 并在您的插件合同中公开它。
Kibana 是一个单页应用,开发人员应该遵循一套简单的规则,以确保从 Kibana 的一个地方导航到另一个地方时不会发生页面重新加载。
例如,使用原生浏览器 API 进行导航会导致整个页面重新加载。
const urlToADashboard = core.http.basePath.prepend(`/dashboard/my-dashboard`);
// this would cause a full page reload:
window.location.href = urlToADashboard;
要在不同的 Kibana 应用之间导航而无需页面重新加载(默认情况下),core
中提供了一些 API
这两种方法都提供自定义,例如使用 options
参数在新页面中打开目标。默认情况下,所有选项都是可选的。
在自身上呈现指向不同 Kibana 应用的链接也会导致整个页面重新加载
const myLink = () =>
<a href={urlToADashboard}>Go to Dashboard</a>;
一种解决方法是处理点击事件,阻止浏览器导航并使用 core.application.navigateToApp
API
const MySPALink = () =>
<a
href={urlToADashboard}
onClick={(e) => {
e.preventDefault();
core.application.navigateToApp('dashboard', { path: '/my-dashboard' });
}}
>
Go to Dashboard
</a>;
如果为您的应用中的每个 Kibana 链接都这样做会太麻烦,那么有一个方便的包装器可以帮助您:RedirectAppLinks。
const MyApp = () =>
<RedirectAppLinks coreStart={{application: core.application}}>
{/*...*/}
{/* navigations using this link will happen in SPA friendly way */}
<a href={urlToADashboard}>Go to Dashboard</a>
{/*...*/}
</RedirectAppLinks>
在某些情况下,您可能需要完全重新加载页面。虽然这种情况很少见并且应该避免,但与其实现您自己的导航,不如使用 navigateToUrl
的 forceRedirect
选项。
const MyForcedPageReloadLink = () =>
<a
href={urlToSomeSpecialApp}
onClick={(e) => {
e.preventDefault();
core.application.navigateToUrl('someSpecialApp', { forceRedirect: true });
}}
>
Go to Some Special App
</a>;
如果您还需要绕过默认的 onAppLeave 行为,您可以将 skipUnload
选项设置为 true
。此选项在 navigateToApp
中也可用。
Kibana 应用通常使用 React 和 React Router。在这种情况下,需要遵循的常用规则
- 设置
BrowserRouter
而不是HashRouter
。 - 使用
core
提供的history
实例初始化您的路由器。
这是为了确保 core
知道在您的应用内部触发的导航,以便在需要时采取相应的措施。
Core
的 ScopedHistory 实例。- 用法示例
- 示例插件
相对链接将相对于您的应用的路由(例如:http://localhost5601/app/{{your-app-id}}
)进行解析,并且以 SPA 友好的方式在您的应用中设置内部链接看起来像
import {Link} from 'react-router-dom';
const MyInternalLink = () => <Link to="/my-other-page"></Link>
尽量避免直接使用 window.location
和 window.history
。
相反,请考虑使用 core
提供的 ScopedHistory 实例。
- 这样,
core
将知道在您的应用中触发的位置更改,并且会采取相应的措施。 - 有些插件正在监听位置更改。手动触发位置更改可能会导致无法预测且难以捕捉的错误。
使用 core
的 ScopedHistory 的常见用例
- 读取/写入查询参数或哈希。
- 命令式地在您的应用内触发内部导航。
- 监听浏览器位置更改。
历史上,Kibana 应用在 URL 中存储了大量的应用状态。Kibana 应用今天遵循的最常见的模式是在 rison 格式的 _a
和 _g
查询参数中存储状态。
_g
(global) - 应该在多个应用之间共享和同步的全局 UI 状态。来自 Analyze 组应用的常见示例:时间范围、刷新间隔、pinned 过滤器。_a
(application) - 范围限定为当前应用的 UI 状态。
迁移到 KP 平台后,我们获得了无需页面重新加载的导航。从那时起,不再真正需要遵循 _g
和 _a
分离。您可以决定是否要遵循此模式,或者是否喜欢单个查询参数或其他内容。 稍后将在 在导航之间保持状态 中解释这种分离的必要性。
有一些实用程序可以帮助您实现这种状态同步。
何时应考虑使用状态同步实用程序
- 您希望以与 Analyze 组应用类似的方式将您的应用状态与 URL 同步。
- 您希望开箱即用地遵循平台的 使用浏览器历史记录和位置最佳实践。
- 您希望开箱即用地支持 URL 溢出的
state:storeInSessionStore
转义舱口。 - 如果您想将状态序列化为不同的(非
rison
)格式,您也应该考虑使用它们。 实用程序是可组合的,您可以实现自己的storage
。 - 如果您想将状态的一部分与 URL 同步,但将另一部分与浏览器存储同步。
何时不应使用状态同步实用程序
- 向 URL 添加查询参数标志或简单的键/值对。
请关注这些文档以了解更多信息。
考虑以下场景
- 您在 Dashboard 应用中查看应用了一些过滤器的仪表板;
- 使用应用内导航导航到 Discover;
- 更改时间过滤器'
- 使用应用内导航导航到 Dashboard。
您会注意到您被导航到 Dashboard 应用,其状态与您离开时的相同,只是时间过滤器已更改为您在 Discover 应用上应用的时间过滤器。
历史上,Kibana Analyze 组应用通过依赖于 URL 中的状态来实现这种行为。 如果您仔细观察导航中的链接,您会注意到状态存储在该链接中,并且每当发生相关的状态更改时,它也会更新
这就是 分离 到 _a
和 _g
查询参数的地方。 被认为是 global 状态的内容会在这些导航链接中不断更新。 在上面的示例中,它是一个时间过滤器。 这是由 KbnUrlTracker 实用程序支持的。 您可以使用它来实现类似的行为。
迁移到 KP 后,导航无需页面重新加载,并且所有插件同时加载。 因此,除非您想通过 URL 完成,否则可能存在更简单的方法来保持应用程序的状态。