Skip to main content

Vue 导航

本指南介绍了路由如何在使用 Ionic 和 Vue 构建的应用中工作。

¥This guide covers how routing works in an app built with Ionic and Vue.

IonRouterOutlet 组件在底层使用了流行的 Vue 路由 库。使用 Ionic 和 Vue Router,你可以创建具有丰富页面过渡的多页面应用。

¥The IonRouterOutlet component uses the popular Vue Router library under the hood. With Ionic and Vue Router, you can create multi-page apps with rich page transitions.

你所了解的有关使用 Vue Router 进行路由的所有知识都会延续到 Ionic Vue 中。让我们看一下 Ionic Vue 应用的基础知识以及路由如何与其配合使用。

¥Everything you know about routing using Vue Router carries over into Ionic Vue. Let's take a look at the basics of an Ionic Vue app and how routing works with it.

简要说明

¥A Brief Note

在阅读本指南时,你可能会注意到大多数概念与不带 Ionic Framework 的 Vue Router 中的概念非常相似。你的观察是正确的!Ionic Vue 利用 Vue Router 的最佳部分,尽可能无缝地过渡到使用 Ionic Framework 构建应用。因此,我们建议尽可能依赖 Vue Router 功能,而不是尝试构建自己的路由解决方案。

¥While reading this guide, you may notice that most of these concepts are very similar to the concepts found in Vue Router without Ionic Framework. Your observation would be correct! Ionic Vue leverages the best parts of Vue Router to make the transition to building apps with Ionic Framework as seamless as possible. As a result, we recommend relying on Vue Router features as much as possible rather than trying to build your own routing solutions.

简单的路由

¥A Simple Route

以下是定义到 "/home" URL 的单个路由的示例路由配置。当你访问 "/home" 时,路由会渲染 HomePage 组件。

¥Here is a sample routing configuration that defines a single route to the "/home" URL. When you visit "/home", the route renders the HomePage component.

router/index.ts



import { createRouter, createWebHistory } from '@ionic/vue-router';


import { RouteRecordRaw } from 'vue-router';


import HomePage from '@/views/Home.vue';



const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'Home',
component: HomePage,
},
];

const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});

export default router;

在应用初始加载时,应用将呈现 HomePage 组件,因为这是此处配置的组件。

¥On the app's initial load, the app will render the HomePage component as that is what is configured here.

处理重定向

¥Handling Redirects

如果我们想在初始负载上选择不同的路径怎么办?为此,我们可以使用路由重定向。重定向的工作方式与典型的路由对象相同,但仅包含一些不同的键:

¥What if we wanted to land a different path on our initial load? For this, we can use router redirects. Redirects work the same way that a typical route object does, but just includes some different keys:

const routes: Array<RouteRecordRaw> = [
{
path: '/',
redirect: '/home',
},
{
path: '/home',
name: 'Home',
component: HomePage,
},
];

在重定向中,我们查找应用的索引路径。然后,如果我们加载它,我们就会重定向到 home 路由。

¥In our redirect, we look for the index path of our app. Then if we load that, we redirect to the home route.

¥Navigating to Different Routes

这一切都很棒,但是如何实际导航到路由呢?为此,我们可以使用 router-link 属性。让我们创建一个新的路由设置:

¥This is all great, but how does one actually navigate to a route? For this, we can use the router-link property. Let's create a new routing setup:

const routes: Array<RouteRecordRaw> = [
{
path: '/',
redirect: '/home',
},
{
path: '/home',
name: 'Home',
component: HomePage,
},
{
path: '/detail',
name: 'Detail',
component: DetailPage,
},
];

假设我们从 home 路由开始,我们想要添加一个按钮,将我们带到 detail 路由。我们可以使用以下 HTML 导航到 detail 路由:

¥Say we start on the home route, and we want to add a button that takes us to the detail route. We can do this using the following HTML to navigate to the detail route:

<ion-button router-link="/detail">Go to detail</ion-button>

我们还可以使用路由 API 以编程方式在我们的应用中导航:

¥We can also programmatically navigate in our app by using the router API:

<template>
<ion-page>
<ion-content>
<ion-button @click="() => router.push('/detail')">Go to detail</ion-button>
</ion-content>
</ion-page>
</template>

<script lang="ts">
import { IonButton, IonContent, IonPage } from '@ionic/vue';
import { defineComponent } from 'vue';
import { useRouter } from 'vue-router';

export default defineComponent({
name: 'HomePage',
components: {
IonButton,
IonContent,
IonPage,
},
setup() {
const router = useRouter();
return { router };
},
});
</script>

两个选项都提供相同的导航机制,只是适合不同的用例。

¥Both options provide the same navigation mechanism, just fitting different use cases.

¥Navigating using router-link

router-link 属性可以在任何 Ionic Vue 组件上设置,当单击该组件时,路由将导航到指定的路由。router-link 属性接受字符串值以及命名路由,就像 Vue Router 中的 router.push 一样。对于附加控制,还可以设置 router-directionrouter-animation 属性。

¥The router-link attribute can be set on any Ionic Vue component, and the router will navigate to the route specified when the component is clicked. The router-link attribute accepts string values as well as named routes, just like router.push from Vue Router. For additional control, the router-direction and router-animation attributes can be set as well.

router-direction 属性接受 forwardbacknone 的值,用于控制页面转换的方向。

¥The router-direction attribute accepts values of forward, back, or none and is used to control the direction of the page transition.

router-animation 属性接受 AnimationBuilder 函数,用于提供自定义页面转换,该转换仅在单击提供该转换的组件时使用。AnimationBuilder 类型是一个返回 Ionic Animation 实例的函数。有关在 Ionic Vue 中使用动画的更多信息,请参阅 动画文档

¥The router-animation attribute accepts an AnimationBuilder function and is used to provide a custom page transition that is only used when clicking the component it is provided on. The AnimationBuilder type is a function that returns an Ionic Animation instance. See the Animations documentation for more information on using animations in Ionic Vue.

<ion-button router-link="/page2" router-direction="back" :router-animation="myAnimation">Click Me</ion-button>

¥Navigating using useIonRouter

使用 router-link 的一个缺点是你无法在导航之前运行自定义代码。这使得诸如在导航之前发出网络请求之类的任务变得困难。你可以直接使用 Vue Router,但是这样你就失去了控制页面转换的能力。这就是 useIonRouter 实用程序有用的地方。

¥One downside of using router-link is that you cannot run custom code prior to navigating. This makes tasks such as firing off a network request prior to navigation difficult. You could use Vue Router directly, but then you lose the ability to control the page transition. This is where the useIonRouter utility is helpful.

useIonRouter 实用程序是一种提供编程导航方法的功能,同时可以完全控制页面转换。这使得在导航之前运行自定义代码变得容易。

¥The useIonRouter utility is a function that provides methods for programmatic navigation while having full control over the page transitions. This makes it easy to run custom code before navigating.

第一个示例让我们使用自定义页面转换将新页面推送到堆栈上:

¥This first example lets us push a new page onto the stack with a custom page transition:

import { defineComponent } from 'vue';


import { useIonRouter } from '@ionic/vue';




import { customAnimation } from '@/animations/customAnimation';



export default defineComponent({
...,
setup() {
const ionRouter = useIonRouter();

ionRouter.push('/page2', customAnimation);
}
});

useIonRouter 提供了方便的 pushreplacebackforward 方法,可以轻松使用常见的导航操作。它还提供了 navigate 方法,可以用于更复杂的导航场景:

¥useIonRouter provides convenience push, replace, back, and forward methods to make it easy to use common navigation actions. It also provides a navigate method which can be used in more complex navigation scenarios:

import { defineComponent } from 'vue';


import { useIonRouter } from '@ionic/vue';




import { customAnimation } from '@/animations/customAnimation';



export default defineComponent({
...,
setup() {
const ionRouter = useIonRouter();

ionRouter.navigate('/page2', 'forward', 'replace', customAnimation);
}
});

上面的示例让应用通过使用向前方向的自定义动画导航到 /page2。此外,replace 值可确保应用在导航时替换当前历史记录条目。

¥The example above has the app navigate to /page2 with a custom animation that uses the forward direction. In addition, the replace value ensures that the app replaces the current history entry when navigating.

注意

useIonRouter 使用 Vue inject() 函数,并且只能在 setup() 函数内部使用。

¥useIonRouter uses the Vue inject() function and should only be used inside of your setup() function.

请参阅 使用 IonRouter 文档 了解更多详细信息以及类型信息。

¥See the useIonRouter documentation for more details as well as type information.

¥Navigating using router.go

Vue Router 有一个 router.go 方法,允许开发者在应用历史记录中向前或向后移动。让我们看一个例子。

¥Vue Router has a router.go method that allows developers to move forward or backward through the application history. Let's take a look at an example.

假设你有以下应用历史记录:

¥Say you have the following application history:

/pageA --> /pageB --> /pageC

如果你在 /pageC 上调用 router.go(-2),你将被带回到 /pageA。如果你随后调用 router.go(2),你将被带到 /pageC

¥If you were to call router.go(-2) on /pageC, you would be brought back to /pageA. If you then called router.go(2), you would be brought to /pageC.

router.go() 的一个关键特性是它期望你的应用历史记录是线性的。这意味着 router.go() 不应在使用非线性路由的应用中使用。请参阅 线性路由与非线性路由 了解更多信息。

¥A key characteristic of router.go() is that it expects your application history to be linear. This means that router.go() should not be used in applications that make use of non-linear routing. See Linear Routing versus Non-Linear Routing for more information.

延迟加载路由

¥Lazy Loading Routes

目前我们的路由设置方式使得它们在加载应用时包含在相同的初始块中,这并不总是理想的。相反,我们可以设置路由,以便在需要时加载组件:

¥The current way our routes are setup makes it so they are included in the same initial chunk when loading the app, which is not always ideal. Instead, we can set up our routes so that components are loaded as they are needed:

const routes: Array<RouteRecordRaw> = [
{
path: '/',
redirect: '/home',
},
{
path: '/home',
name: 'Home',
component: HomePage,
},
{
path: '/detail',
name: 'Detail',
component: () => import('@/views/DetailPage.vue'),
},
];

在这里,我们的设置与之前相同,只是这次 DetailPage 被替换为导入调用。这将导致 DetailPage 组件不再是应用加载时请求的块的一部分。

¥Here, we have the same setup as before only this time DetailPage has been replaced with an import call. This will result in the DetailPage component no longer being part of the chunk that is requested on application load.

线性路由与非线性路由

¥Linear Routing versus Non-Linear Routing

线性路由

¥Linear Routing

如果你构建了一个使用路由的 Web 应用,那么你以前可能使用过线性路由。线性路由意味着你可以通过推送和弹出页面在应用历史记录中向前或向后移动。

¥If you have built a web app that uses routing, you likely have used linear routing before. Linear routing means that you can move forward or backward through the application history by pushing and popping pages.

以下是移动应用中线性路由的示例:

¥The following is an example of linear routing in a mobile app:

本例中的应用历史记录具有以下路径:

¥The application history in this example has the following path:

Accessibility --> VoiceOver --> Speech

当我们按下后退按钮时,我们会遵循相同的路由路径,但方向相反。线性路由很有用,因为它允许简单且可预测的路由行为。这也意味着我们可以使用 Vue Router API,例如 router.go()

¥When we press the back button, we follow that same routing path except in reverse. Linear routing is helpful in that it allows for simple and predictable routing behaviors. It also means we can use Vue Router APIs such as router.go().

线性路由的缺点是它不允许复杂的用户体验,例如选项卡视图。这就是非线性路由发挥作用的地方。

¥The downside of linear routing is that it does not allow for complex user experiences such as tab views. This is where non-linear routing comes into play.

非线性路由

¥Non-Linear Routing

对于许多学习使用 Ionic 构建移动应用的 Web 开发者来说,非线性路由可能是一个新概念。

¥Non-linear routing is a concept that may be new to many web developers learning to build mobile apps with Ionic.

非线性路由意味着用户应该返回的视图不一定是屏幕上显示的前一个视图。

¥Non-linear routing means that the view that the user should go back to is not necessarily the previous view that was displayed on the screen.

以下是非线性路由的示例:

¥The following is an example of non-linear routing:

在上面的示例中,我们从 Originals 选项卡开始。点击卡片即可进入 Originals 选项卡中的 Ted Lasso 视图。

¥In the example above, we start on the Originals tab. Tapping a card brings us to the Ted Lasso view within the Originals tab.

从这里,我们切换到 Search 选项卡。然后,我们再次点击 Originals 选项卡,返回到 Ted Lasso 视图。至此,我们已经开始使用非线性路由。

¥From here, we switch to the Search tab. Then, we tap the Originals tab again and are brought back to the Ted Lasso view. At this point, we have started using non-linear routing.

为什么这是非线性路由?我们之前看到的视图是 Search 视图。然而,按 Ted Lasso 视图上的后退按钮应该会让我们回到根 Originals 视图。发生这种情况是因为移动应用中的每个选项卡都被视为自己的堆栈。使用选项卡 部分对此进行了更详细的讨论。

¥Why is this non-linear routing? The previous view we were on was the Search view. However, pressing the back button on the Ted Lasso view should bring us back to the root Originals view. This happens because each tab in a mobile app is treated as its own stack. The Working with Tabs sections goes over this in more detail.

如果从 Ted Lasso 视图中点击简单地称为 router.go(-1) 的后退按钮,我们将返回到 Search 视图,这是不正确的。

¥If tapping the back button simply called router.go(-1) from the Ted Lasso view, we would be brought back to the Search view which is not correct.

非线性路由允许线性路由无法处理的复杂用户流。然而,某些线性路由 API(例如 router.go())不能在这种非线性环境中使用。这意味着在使用选项卡或嵌套插座时不应使用 router.go()

¥Non-linear routing allows for sophisticated user flows that linear routing cannot handle. However, certain linear routing APIs such as router.go() cannot be used in this non-linear environment. This means that router.go() should not be used when using tabs or nested outlets.

我应该选择哪一个?

¥Which one should I choose?

我们建议你的应用尽可能简单,直到你需要添加非线性路由。非线性路由非常强大,但它也给移动应用增加了相当多的复杂性。

¥We recommend keeping your application as simple as possible until you need to add non-linear routing. Non-linear routing is very powerful, but it also adds a considerable amount of complexity to mobile applications.

非线性布线的两个最常见用途是使用制表符和嵌套 ion-router-outlet。如果你的应用满足选项卡或嵌套路由出口用例,我们建议仅使用非线性路由。

¥The two most common uses of non-linear routing is with tabs and nested ion-router-outlets. We recommend only using non-linear routing if your application meets the tabs or nested router outlet use cases.

有关选项卡的更多信息,请参阅 使用选项卡

¥For more on tabs, please see Working with Tabs.

有关嵌套路由插座的更多信息,请参阅 嵌套路由

¥For more on nested router outlets, please see Nested Routes.

共享 URL 与嵌套路由

¥Shared URLs versus Nested Routes

设置路由时一个常见的混淆点是在共享 URL 或嵌套路由之间做出选择。本指南的这一部分将解释两者并帮助你决定使用哪一个。

¥A common point of confusion when setting up routing is deciding between shared URLs or nested routes. This part of the guide will explain both and help you decide which one to use.

共享网址

¥Shared URLs

共享 URL 是一种路由配置,其中路由具有公共的 URL 片段。以下是共享 URL 配置的示例:

¥Shared URLs is a route configuration where routes have pieces of the URL in common. The following is an example of a shared URL configuration:

const routes: Array<RouteRecordRaw> = [
{
path: '/dashboard',
component: DashboardMainPage,
},
{
path: '/dashboard/stats',
component: DashboardStatsPage,
},
];

上述路由被视为 "shared",因为它们重用了 URL 的 dashboard 部分。

¥The above routes are considered "shared" because they reuse the dashboard piece of the URL.

嵌套路由

¥Nested Routes

嵌套路由是一种路由配置,其中路由被列为其他路由的子路由。以下是嵌套路由配置的示例:

¥Nested Routes is a route configuration where routes are listed as children of other routes. The following is an example of a nested route configuration:

const routes: Array<RouteRecordRaw> = [
{
path: '/dashboard/:id',
component: DashboardRouterOutlet,
children: [
{
path: '',
component: DashboardMainPage,
},
{
path: 'stats',
component: DashboardStatsPage,
},
],
},
];

上述路由是嵌套的,因为它们位于父路由的 children 数组中。请注意,父路由呈现 DashboardRouterOutlet 组件。当你嵌套路由时,你需要渲染 ion-router-outlet 的另一个实例。

¥The above routes are nested because they are in the children array of the parent route. Notice that the parent route renders the DashboardRouterOutlet component. When you nest routes, you need to render another instance of ion-router-outlet.

我应该选择哪一个?

¥Which one should I choose?

当你想要从页面 A 转换到页面 B 同时保留 URL 中两个页面之间的关系时,共享 URL 非常有用。在我们前面的示例中,/dashboard 页面上的按钮可以转换到 /dashboard/stats 页面。由于 a) 页面转换和 b) url,两个页面之间的关系得以保留。

¥Shared URLs are great when you want to transition from page A to page B while preserving the relationship between the two pages in the URL. In our previous example, a button on the /dashboard page could transition to the /dashboard/stats page. The relationship between the two pages is preserved because of a) the page transition and b) the url.

当你想要渲染插座 A 中的内容同时渲染嵌套插座 B 内的子内容时,应使用嵌套路由。你将遇到的最常见的用例是选项卡。当你加载选项卡 Ionic 入门应用时,你将看到第一个 ion-router-outlet 中呈现的 ion-tab-barion-tabs 组件。ion-tabs 组件呈现另一个 ion-router-outlet,它负责呈现每个选项卡的内容。

¥Nested routes should be used when you want to render content in outlet A while also rendering sub-content inside of a nested outlet B. The most common use case you will run into is tabs. When you load up a tabs Ionic starter application, you will see ion-tab-bar and ion-tabs components rendered in the first ion-router-outlet. The ion-tabs component renders another ion-router-outlet which is responsible for rendering the contents of each tab.

嵌套路由在移动应用中有意义的用例很少。如有疑问,请使用共享 URL 路由配置。我们强烈警告不要在选项卡以外的上下文中使用嵌套路由,因为它很快就会使你的应用导航变得混乱。

¥There are very few use cases in which nested routes make sense in mobile applications. When in doubt, use the shared URL route configuration. We strongly caution against using nested routing in contexts other than tabs as it can quickly make navigating your app confusing.

使用选项卡

¥Working with Tabs

使用选项卡时,Ionic Vue 需要一种方法来知道哪个视图属于哪个选项卡。IonTabs 组件在这里派上用场,但让我们看看它的路由设置是什么样的:

¥When working with tabs, Ionic Vue needs a way to know which view belongs to which tab. The IonTabs component comes in handy here, but let's look at what the routing setup for this looks like:

const routes: Array<RouteRecordRaw> = [
{
path: '/',
redirect: '/tabs/tab1',
},
{
path: '/tabs/',
component: Tabs,
children: [
{
path: '',
redirect: 'tab1',
},
{
path: 'tab1',
component: () => import('@/views/Tab1.vue'),
},
{
path: 'tab2',
component: () => import('@/views/Tab2.vue'),
},
{
path: 'tab3',
component: () => import('@/views/Tab3.vue'),
},
],
},
];

在这里,我们的 tabs 路径加载了 Tabs 组件。我们将每个选项卡作为 children 数组内的路由对象提供。在此示例中,我们将路径称为 tabs,但这可以自定义。

¥Here, our tabs path loads a Tabs component. We provide each tab as a route object inside of the children array. In this example, we call the path tabs, but this can be customized.

让我们首先看一下我们的 Tabs 组件:

¥Let's start by taking a look at our Tabs component:

<template>
<ion-page>
<ion-tabs>
<ion-router-outlet></ion-router-outlet>
<ion-tab-bar slot="bottom">
<ion-tab-button tab="tab1" href="/tabs/tab1">
<ion-icon :icon="triangle" />
<ion-label>Tab 1</ion-label>
</ion-tab-button>

<ion-tab-button tab="tab2" href="/tabs/tab2">
<ion-icon :icon="ellipse" />
<ion-label>Tab 2</ion-label>
</ion-tab-button>

<ion-tab-button tab="tab3" href="/tabs/tab3">
<ion-icon :icon="square" />
<ion-label>Tab 3</ion-label>
</ion-tab-button>
</ion-tab-bar>
</ion-tabs>
</ion-page>
</template>

<script lang="ts">
import { IonTabBar, IonTabButton, IonTabs, IonLabel, IonIcon, IonPage, IonRouterOutlet } from '@ionic/vue';
import { ellipse, square, triangle } from 'ionicons/icons';

export default {
name: 'Tabs',
components: {
IonLabel,
IonTabs,
IonTabBar,
IonTabButton,
IonIcon,
IonPage,
IonRouterOutlet,
},
setup() {
return {
ellipse,
square,
triangle,
};
},
};
</script>

如果你以前使用过 Ionic Framework,那么这应该感觉很熟悉。我们创建一个 ion-tabs 组件并提供一个 ion-tab-barion-tab-bar 提供 ion-tab-button 组件,每个组件都有一个 tab 属性,该属性与其在路由配置中的相应选项卡关联。我们还提供了 ion-router-outlet 来为 ion-tabs 提供渲染不同选项卡视图的出口。

¥If you have worked with Ionic Framework before, this should feel familiar. We create an ion-tabs component and provide an ion-tab-bar. The ion-tab-bar provides ion-tab-button components, each with a tab property that is associated with its corresponding tab in the router config. We also provide an ion-router-outlet to give ion-tabs an outlet to render the different tab views in.

Ionic 中的选项卡工作原理

¥How Tabs in Ionic Work

Ionic 中的每个选项卡都被视为一个单独的导航堆栈。这意味着如果你的应用中有三个选项卡,则每个选项卡都有自己的导航堆栈。在每个堆栈中,你可以向前导航(推送视图)和向后导航(弹出视图)。

¥Each tab in Ionic is treated as an individual navigation stack. This means if you have three tabs in your application, each tab has its own navigation stack. Within each stack you can navigate forwards (push a view) and backwards (pop a view).

请务必注意此行为,因为它与其他基于 Web UI 库中的大多数选项卡实现不同。其他库通常将选项卡作为一个历史堆栈进行管理。

¥This behavior is important to note as it is different than most tab implementations that are found in other web based UI libraries. Other libraries typically manage tabs as one single history stack.

由于 Ionic 专注于帮助开发者构建移动应用,因此 Ionic 中的选项卡旨在尽可能匹配原生移动选项卡。因此,Ionic 选项卡中的某些行为可能与你在其他 UI 库中看到的选项卡实现不同。请继续阅读以了解有关其中一些差异的更多信息。

¥Since Ionic is focused on helping developers build mobile apps, the tabs in Ionic are designed to match native mobile tabs as closely as possible. As a result, there may be certain behaviors in Ionic's tabs that differ from tabs implementations you have seen in other UI libraries. Read on to learn more about some of these differences.

选项卡中的子路由

¥Child Routes within Tabs

向选项卡添加其他路由时,应将它们编写为同级路由,并以父选项卡作为路径前缀。下面的示例将 /tabs/tab1/view 路由定义为 /tabs/tab1 路由的同级路由。由于这条新路由具有 tab1 前缀,因此它将在 Tabs 组件内部渲染,并且 Tab 1 仍将在 ion-tab-bar 中被选中。

¥When adding additional routes to tabs you should write them as sibling routes with the parent tab as the path prefix. The example below defines the /tabs/tab1/view route as a sibling of the /tabs/tab1 route. Since this new route has the tab1 prefix, it will be rendered inside of the Tabs component, and Tab 1 will still be selected in the ion-tab-bar.

const routes: Array<RouteRecordRaw> = [
{
path: '/',
redirect: '/tabs/tab1',
},
{
path: '/tabs/',
component: Tabs,
children: [
{
path: '',
redirect: 'tab1',
},
{
path: 'tab1',
component: () => import('@/views/Tab1.vue'),
},
{
path: 'tab1/view',
component: () => import('@/views/Tab1View.vue'),
},
{
path: 'tab2',
component: () => import('@/views/Tab2.vue'),
},
{
path: 'tab3',
component: () => import('@/views/Tab3.vue'),
},
],
},
];

在选项卡之间切换

¥Switching Between Tabs

由于每个选项卡都是其自己的导航堆栈,因此需要注意的是,这些导航堆栈永远不应该交互。这意味着选项卡 1 中不应该有将用户路由到选项卡 2 的按钮。换句话说,选项卡只能通过用户点击选项卡栏中的选项卡按钮来更改。

¥Since each tab is its own navigation stack, it is important to note that these navigation stacks should never interact. This means that there should never be a button in Tab 1 that routes a user to Tab 2. In other words, tabs should only be changed by the user tapping a tab button in the tab bar.

实践中一个很好的例子是 iOS App Store 和 Google Play Store 移动应用。这些应用都提供选项卡式界面,但都没有跨选项卡引导用户。例如,iOS App Store 应用中的 "游戏" 选项卡永远不会将用户定向到 "搜索" 选项卡,反之亦然。

¥A good example of this in practice is the iOS App Store and Google Play Store mobile applications. These apps both provide tabbed interfaces, but neither one ever routes the user across tabs. For example, the "Games" tab in the iOS App Store app never directs users to the "Search" tab and vice versa.

让我们看一下使用选项卡时常犯的几个常见错误。

¥Let's take a look at a couple common mistakes that are made with tabs.

多个选项卡引用的设置选项卡

¥A Settings Tab That Multiple Tabs Reference

常见的做法是创建“设置”视图作为其自己的选项卡。如果开发者需要呈现多个嵌套设置菜单,这非常有用。但是,其他选项卡绝不应尝试路由到“设置”选项卡。正如我们上面提到的,激活“设置”选项卡的唯一方法是用户点击相应的选项卡按钮。

¥A common practice is to create a Settings view as its own tab. This is great if developers need to present several nested settings menus. However, other tabs should never try to route to the Settings tab. As we mentioned above, the only way that the Settings tab should be activated is by a user tapping the appropriate tab button.

如果你发现你的选项卡需要引用“设置”选项卡,我们建议使用 ion-modal 将“设置”视图设为模式。这是在 iOS App Store 应用中发现的做法。通过这种方法,任何选项卡都可以呈现模式,而不会破坏每个选项卡作为其自己的堆栈的移动选项卡模式。

¥If you find that your tabs need to reference the Settings tab, we recommend making the Settings view a modal by using ion-modal. This is a practice found in the iOS App Store app. With this approach, any tab can present the modal without breaking the mobile tabs pattern of each tab being its own stack.

下面的示例显示了 iOS App Store 应用如何处理从多个选项卡呈现 "账户" 视图。通过在模式中呈现 "账户" 视图,应用可以在移动选项卡最佳实践中工作,以跨多个选项卡显示相同的视图。

¥The example below shows how the iOS App Store app handles presenting an "Account" view from multiple tabs. By presenting the "Account" view in a modal, the app can work within the mobile tabs best practices to show the same view across multiple tabs.

跨选项卡重用视图

¥Reusing Views Across Tabs

另一种常见做法是在多个选项卡中呈现相同的视图。开发者通常尝试通过将视图包含在单个选项卡中,并将其他选项卡路由到该选项卡来实现此目的。正如我们上面提到的,这打破了移动标签模式,应该避免。

¥Another common practice is to present the same view in multiple tabs. Developers often try to do this by having the view contained in a single tab, with other tabs routing to that tab. As we mentioned above, this breaks the mobile tabs pattern and should be avoided.

相反,我们建议在每个选项卡中包含引用相同组件的路由。这是 Spotify 等流行应用中的做法。例如,你可以从 "家"、"搜索" 和 "你的库" 选项卡访问专辑或播客。访问专辑或播客时,用户会停留在该选项卡中。该应用通过为每个选项卡创建路由并共享代码库中的通用组件来实现此目的。

¥Instead, we recommend having routes in each tab that reference the same component. This is a practice done in popular apps like Spotify. For example, you can access an album or podcast from the "Home", "Search", and "Your Library" tabs. When accessing the album or podcast, users stay within that tab. The app does this by creating routes per tab and sharing a common component in the codebase.

下面的示例显示了 Spotify 应用如何重用相同的专辑组件来在多个选项卡中显示内容。请注意,每个屏幕截图都显示相同的专辑,但来自不同的选项卡。

¥The example below shows how the Spotify app reuses the same album component to show content in multiple tabs. Notice that each screenshot shows the same album but from a different tab.

主页选项卡搜索选项卡
<img src=/img/usage/tabs-home.jpg /><img src=/img/usage/tabs-search.jpg />

组件

¥Components

IonRouterOutlet

IonRouterOutlet 组件提供了一个容器来渲染视图。它与其他 Vue 应用中的 RouterView 组件类似,只是 IonRouterOutlet 可以在同一出口中渲染 DOM 中的多个页面。当一个组件在 IonRouterOutlet 中渲染时,我们认为这是一个 Ionic Framework "page"。路由出口容器控制页面之间的过渡动画以及控制页面的创建和销毁。这有助于在视图之间来回切换时保持视图之间的状态。

¥The IonRouterOutlet component provides a container to render your views in. It is similar to the RouterView component found in other Vue applications except that IonRouterOutlet can render multiple pages in the DOM in the same outlet. When a component is rendered in IonRouterOutlet we consider this to be an Ionic Framework "page". The router outlet container controls the transition animation between the pages as well as controls when a page is created and destroyed. This helps maintain the state between the views when switching back and forth between them.

在模板中设置 IonRouterOutlet 时,不应在 IonRouterOutlet 内提供任何内容。虽然 IonRouterOutlet 可以嵌套在子组件中,但我们警告不要这样做,因为它通常会使应用中的导航变得混乱。请参阅 共享 URL 与嵌套路由 了解更多信息。

¥Nothing should be provided inside of IonRouterOutlet when setting it up in your template. While IonRouterOutlet can be nested in child components, we caution against it as it typically makes navigation in apps confusing. See Shared URLs versus Nested Routes for more information.

IonPage

IonPage 组件将每个视图封装在 Ionic Vue 应用中,并允许页面转换和堆栈导航正常工作。使用路由导航到的每个视图都必须包含 IonPage 组件。

¥The IonPage component wraps each view in an Ionic Vue app and allows page transitions and stack navigation to work properly. Each view that is navigated to using the router must include an IonPage component.

<template>
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Home</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">Hello World</ion-content>
</ion-page>
</template>

<script lang="ts">
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/vue';
import { defineComponent } from 'vue';

export default defineComponent({
components: {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
},
});
</script>

通过 IonModalIonPopover 呈现的组件通常不需要 IonPage 组件,除非你需要封装器元素。在这种情况下,我们建议使用 IonPage,以便仍然可以正确计算组件尺寸。

¥Components presented via IonModal or IonPopover do not typically need an IonPage component unless you need a wrapper element. In that case, we recommend using IonPage so that the component dimensions are still computed properly.

函数

¥Functions

useIonRouter

▸ 使用 IonRouter():UseIonRouterResult

¥▸ useIonRouter(): UseIonRouterResult

返回 Ionic 路由实例,包含用于导航、自定义页面转换和原生功能的路由上下文的 API 方法。该函数可以与 Vue 中的 useRouter 函数结合使用。

¥Returns the Ionic router instance, containing API methods for navigating, customizing page transitions and routing context for native features. This function can be used in combination with the useRouter function from Vue.

用法示例请参考我们的 实用函数

¥For example usages, please refer to our Utility Functions.

网址参数

¥URL Parameters

让我们扩展原来的路由示例来展示如何使用 URL 参数:

¥Let's expand upon our original routing example to show how we can use URL parameters:

const routes: Array<RouteRecordRaw> = [
{
path: '/',
redirect: '/home',
},
{
path: '/home',
name: 'Home',
component: HomePage,
},
{
path: '/detail/:id',
name: 'Detail',
component: DetailPage,
},
];

请注意,我们现在已将 :id 添加到 detail 路径字符串的末尾。URL 参数是路由路径的动态部分。当用户导航到诸如 /details/1 之类的 URL 时,"1" 将保存到名为 "id" 的参数中,该参数可以在路由呈现时在组件中访问。

¥Notice that we have now added :id to the end of our detail path string. URL parameters are dynamic portions of our route paths. When the user navigates to a URL such as /details/1 the "1" is saved to a parameter named "id" which can be accessed in the component when the route renders.

让我们看看如何在我们的组件中使用它:

¥Let's look at how to use it in our component:

<template>
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Details</ion-title>
</ion-toolbar>
</ion-header>

<ion-content> Detail ID: {{ id }} </ion-content>
</ion-page>
</template>

<script lang="ts">
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/vue';
import { defineComponent } from 'vue';
import { useRoute } from 'vue-router';

export default defineComponent({
name: 'Detail',
components: {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
},
setup() {
const route = useRoute();
const { id } = route.params;
return { id };
},
});
</script>

我们的 route 变量包含当前路由的一个实例。它还包含我们传入的任何参数。我们可以从这里获取 id 参数并将其显示在屏幕上。

¥Our route variable contains an instance of the current route. It also contains any parameters we have passed in. We can obtain the id parameter from here and display it on the screen.

路由历史记录

¥Router History

Vue Router 附带可配置的历史记录模式。让我们看看不同的选项以及你可能想要使用每个选项的原因。

¥Vue Router ships with a configurable history mode. Let's look at the different options and why you might want to use each one.

  • createWebHistory:此选项创建 HTML5 历史记录。它利用 History API 来实现 URL 导航,而无需重新加载页面。这是单页应用最常见的历史记录模式。如有疑问,请使用 createWebHistory

    ¥createWebHistory: This option creates an HTML5 history. It leverages the History API to achieve URL navigation without a page reload. This is the most common history mode for single page applications. When in doubt, use createWebHistory.

  • createWebHashHistory:此选项将哈希 (#) 添加到你的 URL。这对于没有主机的 Web 应用或当你无法完全控制服务器路由时非常有用。搜索引擎有时会忽略哈希片段,因此如果 SEO 对你的应用很重要,你应该使用 createWebHistory

    ¥createWebHashHistory: This option adds a hash (#) to your URL. This is useful for web applications with no host or when you do not have full control over the server routes. Search engines sometimes ignore hash fragments, so you should use createWebHistory instead if SEO is important for your application.

  • createMemoryHistory:此选项创建基于内存的历史记录。这主要用于处理服务器端渲染(SSR)。

    ¥createMemoryHistory: This option creates an in-memory based history. This is mainly used to handle server-side rendering (SSR).

更多信息

¥More Information

有关使用 Vue Router 在 Vue 中进行路由的更多信息,请查看他们位于 http://router.vuejs.org/ 的文档。

¥For more info on routing in Vue using Vue Router, check out their docs at http://router.vuejs.org/.