],
sidebar: {
- '/zh/api/': [
- {
- text: 'packages',
- items: [{ text: 'vue-router', link: '/zh/api/' }],
- },
- ],
-
'/zh/': [
{
+ text: '设置',
items: [
{
text: '介绍',
text: '嵌套路由',
link: '/zh/guide/essentials/nested-routes.html',
},
- {
- text: '编程式导航',
- link: '/zh/guide/essentials/navigation.html',
- },
{
text: '命名路由',
link: '/zh/guide/essentials/named-routes.html',
},
+ {
+ text: '编程式导航',
+ link: '/zh/guide/essentials/navigation.html',
+ },
{
text: '命名视图',
link: '/zh/guide/essentials/named-views.html',
text: '路由组件传参',
link: '/zh/guide/essentials/passing-props.html',
},
+ {
+ text: '匹配当前路由的链接',
+ link: '/zh/guide/essentials/active-links.html',
+ },
{
text: '不同的历史记录模式',
link: '/zh/guide/essentials/history-mode.html',
],
},
],
+
+ '/zh/api/': [
+ {
+ text: 'packages',
+ items: [{ text: 'vue-router', link: '/zh/api/' }],
+ },
+ ],
},
},
}
{
"zh": {
- "hash": "35a9c1b",
- "date": "2023-12-16"
+ "hash": "d842b6f",
+ "date": "2024-05-17"
}
}
\ No newline at end of file
title="Learn how to use Vue Router with the composition API"
/>
-引入 `setup` 和 Vue 的[组合式 API](https://cn.vuejs.org/guide/extras/composition-api-faq.html),开辟了新的可能性,但要想充分发挥 Vue Router 的潜力,我们需要使用一些新的函数来代替访问 `this` 和组件内导航守卫。
+Vue 的[组合式 API](https://cn.vuejs.org/guide/extras/composition-api-faq.html) 的引入开辟了新的可能性,但要想充分发挥 Vue Router 的潜力,我们需要使用一些新的函数来代替访问 `this` 和组件内导航守卫。
## 在 `setup` 中访问路由和当前路由
-因为我们在 `setup` 里面没有访问 `this`,所以我们不能再直接访问 `this.$router` 或 `this.$route`。作为替代,我们使用 `useRouter` 和 `useRoute` 函数:
+因为我们在 `setup` 里面没有访问 `this`,所以我们不能直接访问 `this.$router` 或 `this.$route`。作为替代,我们使用 `useRouter` 和 `useRoute` 函数:
-```js
+```vue
+<script setup>
import { useRouter, useRoute } from 'vue-router'
-export default {
- setup() {
- const router = useRouter()
- const route = useRoute()
-
- function pushWithQuery(query) {
- router.push({
- name: 'search',
- query: {
- ...route.query,
- ...query,
- },
- })
- }
- },
+const router = useRouter()
+const route = useRoute()
+
+function pushWithQuery(query) {
+ router.push({
+ name: 'search',
+ query: {
+ ...route.query,
+ ...query,
+ },
+ })
}
+</script>
```
-`route` 对象是一个响应式对象,所以它的任何属性都可以被监听,但你应该**避免监听整个 `route`** 对象。在大多数情况下,你应该直接监听你期望改变的参数。
+`route` 对象是一个响应式对象。在多数情况下,你应该**避免监听整个 `route`** 对象,同时直接监听你期望改变的参数。
-```js
+```vue
+<script setup>
import { useRoute } from 'vue-router'
import { ref, watch } from 'vue'
-export default {
- setup() {
- const route = useRoute()
- const userData = ref()
-
- // 当参数更改时获取用户信息
- watch(
- () => route.params.id,
- async newId => {
- userData.value = await fetchUser(newId)
- }
- )
- },
-}
+const route = useRoute()
+const userData = ref()
+
+// 当参数更改时获取用户信息
+watch(
+ () => route.params.id,
+ async newId => {
+ userData.value = await fetchUser(newId)
+ }
+)
+</script>
```
-请注意,在模板中我们仍然可以访问 `$router` 和 `$route`,所以不需要在 `setup` 中返回 `router` 或 `route`。
+请注意,在模板中我们仍然可以访问 `$router` 和 `$route`,所以如果你只在模板中使用这些对象的话,是不需要 `useRouter` 或 `useRoute` 的。
## 导航守卫
-虽然你仍然可以通过 `setup` 函数来使用组件内的导航守卫,但 Vue Router 将更新和离开守卫作为 组合式 API 函数公开:
+Vue Router 将更新和离开守卫作为组合式 API 函数公开:
-```js
+```vue
+<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
import { ref } from 'vue'
-export default {
- setup() {
- // 与 beforeRouteLeave 相同,无法访问 `this`
- onBeforeRouteLeave((to, from) => {
- const answer = window.confirm(
- 'Do you really want to leave? you have unsaved changes!'
- )
- // 取消导航并停留在同一页面上
- if (!answer) return false
- })
-
- const userData = ref()
-
- // 与 beforeRouteUpdate 相同,无法访问 `this`
- onBeforeRouteUpdate(async (to, from) => {
- //仅当 id 更改时才获取用户,例如仅 query 或 hash 值已更改
- if (to.params.id !== from.params.id) {
- userData.value = await fetchUser(to.params.id)
- }
- })
- },
-}
+// 与 beforeRouteLeave 相同,无法访问 `this`
+onBeforeRouteLeave((to, from) => {
+ const answer = window.confirm(
+ 'Do you really want to leave? you have unsaved changes!'
+ )
+ // 取消导航并停留在同一页面上
+ if (!answer) return false
+})
+
+const userData = ref()
+
+// 与 beforeRouteUpdate 相同,无法访问 `this`
+onBeforeRouteUpdate(async (to, from) => {
+ //仅当 id 更改时才获取用户,例如仅 query 或 hash 值已更改
+ if (to.params.id !== from.params.id) {
+ userData.value = await fetchUser(to.params.id)
+ }
+})
+</script>
```
组合式 API 守卫也可以用在任何由 `<router-view>` 渲染的组件中,它们不必像组件内守卫那样直接用在路由组件上。
Vue Router 将 RouterLink 的内部行为作为一个组合式函数 (composable) 公开。它接收一个类似 `RouterLink` 所有 prop 的响应式对象,并暴露底层属性来构建你自己的 `RouterLink` 组件或生成自定义链接:
-```js
+```vue
+<script setup>
import { RouterLink, useLink } from 'vue-router'
import { computed } from 'vue'
-export default {
- name: 'AppLink',
-
- props: {
- // 如果使用 TypeScript,请添加 @ts-ignore
- ...RouterLink.props,
- inactiveClass: String,
- },
-
- setup(props) {
- const {
- // 解析出来的路由对象
- route,
- // 用在链接里的 href
- href,
- // 布尔类型的 ref 标识链接是否匹配当前路由
- isActive,
- // 布尔类型的 ref 标识链接是否严格匹配当前路由
- isExactActive,
- // 导航至该链接的函数
- navigate
- } = useLink(props)
-
- const isExternalLink = computed(
- () => typeof props.to === 'string' && props.to.startsWith('http')
- )
-
- return { isExternalLink, href, navigate, isActive }
- },
-}
+const props = defineProps({
+ // 如果使用 TypeScript,请添加 @ts-ignore
+ ...RouterLink.props,
+ inactiveClass: String,
+})
+
+const {
+ // 解析出来的路由对象
+ route,
+ // 用在链接里的 href
+ href,
+ // 布尔类型的 ref 标识链接是否匹配当前路由
+ isActive,
+ // 布尔类型的 ref 标识链接是否严格匹配当前路由
+ isExactActive,
+ // 导航至该链接的函数
+ navigate
+} = useLink(props)
+
+const isExternalLink = computed(
+ () => typeof props.to === 'string' && props.to.startsWith('http')
+)
+</script>
```
注意在 RouterLink 的 `v-slot` 中可以访问与 `useLink` 组合式函数相同的属性。
## 导航完成后获取数据
-当你使用这种方式时,我们会马上导航和渲染组件,然后在组件的 created 钩子中获取数据。这让我们有机会在数据获取期间展示一个 loading 状态,还可以在不同视图间展示不同的 loading 状态。
+当你使用这种方式时,我们会马上导航和渲染组件,然后在组件中获取数据。这让我们有机会在数据获取期间展示一个 loading 状态,还可以在不同视图间展示不同的 loading 状态。
-假设我们有一个 `Post` 组件,需要基于 `$route.params.id` 获取文章数据:
+假设我们有一个 `Post` 组件,需要基于 `route.params.id` 获取文章数据:
-```html
+::: code-group
+
+```vue [Composition API]
<template>
<div class="post">
<div v-if="loading" class="loading">Loading...</div>
</div>
</div>
</template>
+
+<script setup>
+import { ref, watch } from 'vue'
+import { useRoute } from 'vue-router'
+import { getPost } from './api.js'
+
+const route = useRoute()
+
+const loading = ref(false)
+const post = ref(null)
+const error = ref(null)
+
+// 侦听路由的参数,以便再次获取数据
+watch(() => route.params.id, fetchData, { immediate: true })
+
+async function fetchData(id) {
+ error.value = post.value = null
+ loading.value = true
+
+ try {
+ // 用获取数据的工具函数 / API 包裹器替换 `getPost`
+ post.value = await getPost(id)
+ } catch (err) {
+ error.value = err.toString()
+ } finally {
+ loading.value = false
+ }
+}
+</script>
```
-```js
+```vue [Options API]
+<template>
+ <div class="post">
+ <div v-if="loading" class="loading">Loading...</div>
+
+ <div v-if="error" class="error">{{ error }}</div>
+
+ <div v-if="post" class="content">
+ <h2>{{ post.title }}</h2>
+ <p>{{ post.body }}</p>
+ </div>
+ </div>
+</template>
+
+<script>
+import { getPost } from './api.js'
+
export default {
data() {
return {
}
},
created() {
- // watch 路由的参数,以便再次获取数据
+ // 侦听路由的参数,以便再次获取数据
this.$watch(
- () => this.$route.params,
- () => {
- this.fetchData()
- },
+ () => this.$route.params.id,
+ this.fetchData,
// 组件创建完后获取数据,
- // 此时 data 已经被 observed 了
+ // 此时 data 已经被监听了
{ immediate: true }
)
},
methods: {
- fetchData() {
+ async fetchData(id) {
this.error = this.post = null
this.loading = true
- // replace `getPost` with your data fetching util / API wrapper
- getPost(this.$route.params.id, (err, post) => {
+
+ try {
+ // 用获取数据的工具函数 / API 包裹器替换 `getPost`
+ this.post = await getPost(id)
+ } catch (err) {
+ this.error = err.toString()
+ } finally {
this.loading = false
- if (err) {
- this.error = err.toString()
- } else {
- this.post = post
- }
- })
+ }
},
},
}
+</script>
```
+:::
+
## 在导航完成前获取数据
通过这种方式,我们在导航转入新的路由前获取数据。我们可以在接下来的组件的 `beforeRouteEnter` 守卫中获取数据,当数据获取成功后只调用 `next` 方法:
}
},
beforeRouteEnter(to, from, next) {
- getPost(to.params.id, (err, post) => {
- // `setData` 方法定义在下面的代码中
- next(vm => vm.setData(err, post))
- })
+ try {
+ const post = await getPost(to.params.id)
+ // `setPost` 方法定义在下面的代码中
+ next(vm => vm.setPost(post))
+ } catch (err) {
+ // `setError` 方法定义在下面的代码中
+ next(vm => vm.setError(err))
+ }
},
// 路由改变前,组件就已经渲染完了
// 逻辑稍稍不同
async beforeRouteUpdate(to, from) {
this.post = null
- try {
- this.post = await getPost(to.params.id)
- } catch (error) {
- this.error = error.toString()
- }
+ getPost(to.params.id).then(this.setPost).catch(this.setError)
},
methods: {
- setData(error, post) {
- if (error) {
- this.error = error
- } else {
- this.post = post
- }
+ setPost(post) {
+ this.post = post
+ },
+ setError(err) {
+ this.error = err.toString()
}
}
}
```js
router.addRoute({ path: '/about', component: About })
-// 我们也可以使用 this.$route 或 route = useRoute() (在 setup 中)
+// 我们也可以使用 this.$route 或 useRoute()
router.replace(router.currentRoute.value.fullPath)
```
让我们扩展 RouterLink 来处理外部链接,并在 `AppLink.vue` 文件中添加一个自定义的 `inactive-class`:
-```vue
+::: code-group
+
+```vue [Composition API]
+<script setup>
+import { computed } from 'vue'
+import { RouterLink } from 'vue-router'
+
+defineOptions({
+ inheritAttrs: false,
+})
+
+const props = defineProps({
+ // 如果使用 TypeScript,请添加 @ts-ignore
+ ...RouterLink.props,
+ inactiveClass: String,
+})
+
+const isExternalLink = computed(() => {
+ return typeof props.to === 'string' && props.to.startsWith('http')
+})
+</script>
+
<template>
<a v-if="isExternalLink" v-bind="$attrs" :href="to" target="_blank">
<slot />
</a>
</router-link>
</template>
+```
+```vue [Options API]
<script>
import { RouterLink } from 'vue-router'
},
}
</script>
+
+<template>
+ <a v-if="isExternalLink" v-bind="$attrs" :href="to" target="_blank">
+ <slot />
+ </a>
+ <router-link
+ v-else
+ v-bind="$props"
+ custom
+ v-slot="{ isActive, href, navigate }"
+ >
+ <a
+ v-bind="$attrs"
+ :href="href"
+ @click="navigate"
+ :class="isActive ? activeClass : inactiveClass"
+ >
+ <slot />
+ </a>
+ </router-link>
+</template>
```
+:::
+
如果你喜欢使用渲染函数或创建 `computed` 属性,你可以使用 [Composition API](./composition-api.md) 中的 `useLink` :
```js
例如,根据上面的路由配置,`/posts/new` 这个 URL 将会匹配父路由记录 (`path: '/posts'`) 以及子路由记录 (`path: 'new'`)。
-一个路由匹配到的所有路由记录会暴露为 `$route` 对象(还有在导航守卫中的路由对象)的`$route.matched` 数组。我们需要遍历这个数组来检查路由记录中的 `meta` 字段,但是 Vue Router 还为你提供了一个 `$route.meta` 方法,它是一个非递归合并**所有 `meta`** 字段(从父字段到子字段)的方法。这意味着你可以简单地写
+一个路由匹配到的所有路由记录会暴露为 `route` 对象(还有在导航守卫中的路由对象)的`route.matched` 数组。我们需要遍历这个数组来检查路由记录中的 `meta` 字段,但是 Vue Router 还为你提供了一个 `route.meta` 方法,它是一个非递归合并**所有 `meta`** 字段(从父字段到子字段)的方法。这意味着你可以简单地写
```js
router.beforeEach((to, from) => {
如果你忽略第二个参数: `isNavigationFailure(failure)`,那么就只会检查这个 `failure` 是不是一个 _Navigation Failure_。
:::
-<!-- TODO: translation -->
-
## 全局导航故障
你可以用 [`router.afterEach()` 导航守卫](./navigation-guards.md#Global-After-Hooks)检测全局导航故障:
## 全局解析守卫
-你可以用 `router.beforeResolve` 注册一个全局守卫。这和 `router.beforeEach` 类似,因为它在**每次导航**时都会触发,不同的是,解析守卫刚好会在导航被确认之前、**所有组件内守卫和异步路由组件被解析之后**调用。这里有一个例子,确保用户可以访问[自定义 meta](./meta.md) 属性 `requiresCamera` 的路由:
+你可以用 `router.beforeResolve` 注册一个全局守卫。这和 `router.beforeEach` 类似,因为它在**每次导航**时都会触发,不同的是,解析守卫刚好会在导航被确认之前、**所有组件内守卫和异步路由组件被解析之后**调用。这里有一个例子,根据路由在[元信息](./meta.md)中的 `requiresCamera` 属性确保用户访问摄像头的权限:
```js
router.beforeResolve(async to => {
]
```
-请注意,你也可以通过使用[路径 meta 字段](./meta.md)和全局导航守卫来实现类似的行为。
+当配合[嵌套路由](../essentials/nested-routes)使用时,父路由和子路由都可以使用 `beforeEnter`。如果放在父级路由上,路由在具有相同父级的子路由之间移动时,它不会被触发。例如:
+
+```js
+const routes = [
+ {
+ path: '/user',
+ beforeEnter() {
+ // ...
+ },
+ children: [
+ { path: 'list', component: UserList },
+ { path: 'details', component: UserDetails },
+ ],
+ },
+]
+```
+
+示例中的 `beforeEnter` 在 `/user/list` 和 `/user/details` 之间移动时不会被调用,因为它们共享相同的父级路由。如果我们直接将 `beforeEnter` 守卫放在 `details` 路由上,那么在这两个路由之间移动时就会被调用。
+
+::: tip
+你也可以通过使用[路由元信息字段](./meta.md)和全局导航守卫来实现类似的行为。
+:::
## 组件内的守卫
- `beforeRouteUpdate`
- `beforeRouteLeave`
-```js
-const UserDetails = {
- template: `...`,
+```vue
+<script>
+export default {
beforeRouteEnter(to, from) {
// 在渲染该组件的对应路由被验证前调用
// 不能获取组件实例 `this` !
// 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
},
}
+</script>
```
`beforeRouteEnter` 守卫 **不能** 访问 `this`,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。
### 使用组合 API
-如果你正在使用[组合 API 和 `setup` 函数](https://cn.vuejs.org/api/composition-api-setup.html)来编写组件,你可以通过 `onBeforeRouteUpdate` 和 `onBeforeRouteLeave` 分别添加 update 和 leave 守卫。 请参考[组合 API 部分](./composition-api.md#导航守卫)以获得更多细节。
+如果你正在使用组合式 API 编写组件,你可以通过 `onBeforeRouteUpdate` 和 `onBeforeRouteLeave` 分别添加 update 和 leave 守卫。 请参考[组合式 API 部分](./composition-api.md#导航守卫)以获得更多细节。
## 完整的导航解析流程
--- /dev/null
+<!-- TODO: translation -->
+
+# Active links
+
+It's common for applications to have a navigation component that renders a list of RouterLink components. Within that list, we might want to style links to the currently active route differently from the others.
+
+The RouterLink component adds two CSS classes to active links, `router-link-active` and `router-link-exact-active`. To understand the difference between them, we first need to consider how Vue Router decides that a link is _active_.
+
+## When are links active?
+
+A RouterLink is considered to be ***active*** if:
+
+1. It matches the same route record (i.e. configured route) as the current location.
+2. It has the same values for the `params` as the current location.
+
+If you're using [nested routes](./nested-routes), any links to ancestor routes will also be considered active if the relevant `params` match.
+
+Other route properties, such as the [`query`](../../api/interfaces/RouteLocationNormalized#query), are not taken into account.
+
+The path doesn't necessarily need to be a perfect match. For example, using an [`alias`](./redirect-and-alias#Alias) would still be considered a match, so long as it resolves to the same route record and `params`.
+
+If a route has a [`redirect`](./redirect-and-alias#Redirect), it won't be followed when checking whether a link is active.
+
+## Exact active links
+
+An ***exact*** match does not include ancestor routes.
+
+Let's imagine we have the following routes:
+
+```js
+const routes = [
+ {
+ path: '/user/:username',
+ component: User,
+ children: [
+ {
+ path: 'role/:roleId',
+ component: Role,
+ }
+ ]
+ }
+]
+```
+
+Then consider these two links:
+
+```vue-html
+<RouterLink to="/user/erina">
+ User
+</RouterLink>
+<RouterLink to="/user/erina/role/admin">
+ Role
+</RouterLink>
+```
+
+If the current location path is `/user/erina/role/admin` then these would both be considered _active_, so the class `router-link-active` would be applied to both links. But only the second link would be considered _exact_, so only that second link would have the class `router-link-exact-active`.
+
+## Configuring the classes
+
+The RouterLink component has two props, `activeClass` and `exactActiveClass`, that can be used to change the names of the classes that are applied:
+
+```vue-html
+<RouterLink
+ activeClass="border-indigo-500"
+ exactActiveClass="border-indigo-700"
+ ...
+>
+```
+
+The default class names can also be changed globally by passing the `linkActiveClass` and `linkExactActiveClass` options to `createRouter()`:
+
+```js
+const router = createRouter({
+ linkActiveClass: 'border-indigo-500',
+ linkExactActiveClass: 'border-indigo-700',
+ // ...
+})
+```
+
+See [Extending RouterLink](../advanced/extending-router-link) for more advanced customization techniques using the `v-slot` API.
很多时候,我们需要将给定匹配模式的路由映射到同一个组件。例如,我们可能有一个 `User` 组件,它应该对所有用户进行渲染,但用户 ID 不同。在 Vue Router 中,我们可以在路径中使用一个动态字段来实现,我们称之为 _路径参数_ :
```js
-const User = {
- template: '<div>User</div>',
-}
+import User from './User.vue'
// 这些都会传递给 `createRouter`
const routes = [
现在像 `/users/johnny` 和 `/users/jolyne` 这样的 URL 都会映射到同一个路由。
-_路径参数_ 用冒号 `:` 表示。当一个路由被匹配时,它的 _params_ 的值将在每个组件中以 `this.$route.params` 的形式暴露出来。因此,我们可以通过更新 `User` 的模板来呈现当前的用户 ID:
+_路径参数_ 用冒号 `:` 表示。当一个路由被匹配时,它的 _params_ 的值将在每个组件中以 `route.params` 的形式暴露出来。因此,我们可以通过更新 `User` 的模板来呈现当前的用户 ID:
-```js
-const User = {
- template: '<div>User {{ $route.params.id }}</div>',
-}
+```vue
+<template>
+ <div>
+ <!-- 当前路由可以通过 $route 在模板中访问 -->
+ User {{ $route.params.id }}
+ </div>
+</template>
```
你可以在同一个路由中设置有多个 _路径参数_,它们会映射到 `$route.params` 上的相应字段。例如:
-| 匹配模式 | 匹配路径 | \$route.params |
+| 匹配模式 | 匹配路径 | route.params |
| ------------------------------ | ------------------------ | ---------------------------------------- |
| /users/:username | /users/eduardo | `{ username: 'eduardo' }` |
| /users/:username/posts/:postId | /users/eduardo/posts/123 | `{ username: 'eduardo', postId: '123' }` |
-除了 `$route.params` 之外,`$route` 对象还公开了其他有用的信息,如 `$route.query`(如果 URL 中存在参数)、`$route.hash` 等。你可以在 [API 参考](../../api/#routelocationnormalized)中查看完整的细节。
+除了 `route.params` 之外,`route` 对象还公开了其他有用的信息,如 `route.query`(如果 URL 中存在参数)、`route.hash` 等。你可以在 [API 参考](../../api/#routelocationnormalized)中查看完整的细节。
这个例子的 demo 可以在[这里](https://codesandbox.io/s/route-params-vue-router-examples-mlb14?from-embed&initialpath=%2Fusers%2Feduardo%2Fposts%2F1)找到。
要对同一个组件中参数的变化做出响应的话,你可以简单地 watch `$route` 对象上的任意属性,在这个场景中,就是 `$route.params` :
-```js
-const User = {
- template: '...',
+::: code-group
+
+```vue [Composition API]
+<script setup>
+import { watch } from 'vue'
+import { useRoute } from 'vue-router'
+
+const route = useRoute()
+
+watch(() => route.params.id, (newId, oldId) => {
+ // 对路由变化做出响应...
+})
+</script>
+```
+
+```vue [Options API]
+<script>
+export default {
created() {
this.$watch(
- () => this.$route.params,
- (toParams, previousParams) => {
+ () => this.$route.params.id,
+ (newId, oldId) => {
// 对路由变化做出响应...
}
)
},
}
+</script>
```
-或者,使用 `beforeRouteUpdate` [导航守卫](../advanced/navigation-guards.md),它也可以取消导航:
+:::
-```js
-const User = {
- template: '...',
+或者,使用 `beforeRouteUpdate` [导航守卫](../advanced/navigation-guards.md),它还允许你取消导航:
+
+::: code-group
+
+```vue [Composition API]
+<script setup>
+import { onBeforeRouteUpdate } from 'vue-router'
+// ...
+
+onBeforeRouteUpdate(async (to, from) => {
+ // 对路由变化做出响应...
+ userData.value = await fetchUser(to.params.id)
+})
+</script>
+```
+
+```vue [Options API]
+<script>
+export default {
async beforeRouteUpdate(to, from) {
// 对路由变化做出响应...
this.userData = await fetchUser(to.params.id)
},
+ // ...
}
+</script>
```
+:::
+
## 捕获所有路由或 404 Not found 路由
<VueSchoolLink
```js
const routes = [
- // 将匹配所有内容并将其放在 `$route.params.pathMatch` 下
+ // 将匹配所有内容并将其放在 `route.params.pathMatch` 下
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
- // 将匹配以 `/user-` 开头的所有内容,并将其放在 `$route.params.afterUser` 下
+ // 将匹配以 `/user-` 开头的所有内容,并将其放在 `route.params.afterUser` 下
{ path: '/user-:afterUser(.*)', component: UserGeneric },
]
```
在这个特定的场景中,我们在括号之间使用了[自定义正则表达式](./route-matching-syntax.md#在参数中自定义正则),并将`pathMatch` 参数标记为[可选可重复](./route-matching-syntax.md#可选参数)。这样做是为了让我们在需要的时候,可以通过将 `path` 拆分成一个数组,直接导航到路由:
```js
-this.$router.push({
+router.push({
name: 'NotFound',
// 保留当前路径并删除第一个字符,以避免目标 URL 以 `//` 开头。
params: { pathMatch: this.$route.path.substring(1).split('/') },
// 保留现有的查询和 hash 值,如果有的话
- query: this.$route.query,
- hash: this.$route.hash,
+ query: route.query,
+ hash: route.hash,
})
```
title="Learn about the named routes"
/>
-除了 `path` 之外,你还可以为任何路由提供 `name`。这有以下优点:
-
-- 没有硬编码的 URL
-- `params` 的自动编码/解码。
-- 防止你在 url 中出现打字错误。
-- 绕过路径排序(如显示一个)
+当创建一个路由时,我们可以选择给路由一个 `name`:
```js
const routes = [
{
path: '/user/:username',
- name: 'user',
- component: User,
- },
+ name: 'profile', // [!code highlight]
+ component: User
+ }
]
```
-要链接到一个命名的路由,可以向 `router-link` 组件的 `to` 属性传递一个对象:
+然后我们可以使用 `name` 而不是 `path` 来传递 `to` 属性给 `<router-link>`:
-```html
-<router-link :to="{ name: 'user', params: { username: 'erina' }}">
- User
+```vue-html
+<router-link :to="{ name: 'profile', params: { username: 'erina' } }">
+ User profile
</router-link>
```
-这跟代码调用 `router.push()` 是一回事:
+上述示例将创建一个指向 `/user/erina` 的链接。
-```js
-router.push({ name: 'user', params: { username: 'erina' } })
-```
+- [在演练场上查看](https://play.vuejs.org/#eNqtVVtP2zAU/itWNqlFauNNIB6iUMEQEps0NjH2tOzBtKY1JLZlO6VTlP++4+PcelnFwyRofe7fubaKCiZk/GyjJBKFVsaRiswNZ45faU1q8mRUQUbrko8yuaPwlRfK/LkV1sHXpGHeq9JxMzScGmT19t5xkMaUaR1vOb9VBe+kntgWXz2Cs06O1LbCTwvRW7knGnEm50paRwIYcrEFd1xlkpBVyCQ5lN74ZOJV0Nom5JcnCFRCM7dKyIiOJkSygsNzBZiBmivAI7l0SUipRvuhCfPge7uWHBiGZPctS0iLJv7T2/YutFFPIt+JjgUJPn7DZ32CtWg7PIZ/4BASg7txKE6gC1VKNx69gw6NTqJJ1HQK5iR1vNA52M+8Yrr6OLuD+AuCtbQpBQYK9Oy6NAZAhLI1KKuKvEc69jSp65Tqw/oh3V7f00P9MsdveOWiecE75DDNhXwhiVMXWVRttYbUWdRpE2xOZ0sHxq1v2jl/a5jQyZ042Mv/HKjvt2aGFTCXFWmnAsTcCMkAxw4SHIjG9E2AUtpUusWyFvyVUGCltBsFmJB2W/dHZCHWswdYLwJ/XiulnrNr323zcQeodthDuAHTgmm4aEqCH1zsrBHYLIISheyyqD9Nnp1FK+e0TSgtpX5ZxrBBtNe4PItP4w8Q07oBN+a2mD4a9erPzDN4bzY1iy5BiS742imV2ynT4l8h9hQvz+Pz+COU/pGCdyrkgm/Qt3ddw/5Cms7CLXsSy50k/dJDT8037QTcuq1kWZ6r1y/Ic6bkHdD5is9fDvCf7SZA/m44ZLfmg+QcM0vugvjmxx3fwLsTFmpRwlwdE95zq/LSYwxqn0q5ANgDPUT7GXsm5PLB3mwcl7ZNygPFaqA+NvL6SOo93NP4bFDF9sfh+LThtgxvkF80fyxxy/Ac7U9i/RcYNWrd)。
-在这两种情况下,路由将导航到路径 `/user/erina`。
+使用 `name` 有很多优点:
-完整的例子在[这里](https://github.com/vuejs/vue-router/blob/dev/examples/named-routes/app.js).
+- 没有硬编码的 URL。
+- `params` 的自动编码/解码。
+- 防止你在 URL 中出现打字错误。
+- 绕过路径排序,例如展示一个匹配相同路径但排序较低的路由。
所有路由的命名**都必须是唯一的**。如果为多条路由添加相同的命名,路由器只会保留最后那一条。你可以在[动态路由](../advanced/dynamic-routing.md#Removing-routes)章节了解更多。
+
+Vue Router 有很多其他部分可以传入网址,例如 `router.push()` 和 `router.replace()` 方法。我们将在[编程式导航](./navigation.md)指南中详细介绍这些方法。就像 `to` 属性一样,这些方法也支持通过 `name` 传入网址:
+
+```js
+router.push({ name: 'user', params: { username: 'erina' } })
+```
有时候想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有 `sidebar` (侧导航) 和 `main` (主内容) 两个视图,这个时候命名视图就派上用场了。你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果 `router-view` 没有设置名字,那么默认为 `default`。
-```html
-<router-view class="view left-sidebar" name="LeftSidebar"></router-view>
-<router-view class="view main-content"></router-view>
-<router-view class="view right-sidebar" name="RightSidebar"></router-view>
+```vue-html
+<router-view class="view left-sidebar" name="LeftSidebar" />
+<router-view class="view main-content" />
+<router-view class="view right-sidebar" name="RightSidebar" />
```
一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。确保正确使用 `components` 配置 (带上 **s**):
一些应用程序的 UI 由多层嵌套的组件组成。在这种情况下,URL 的片段通常对应于特定的嵌套组件结构,例如:
```
-/user/johnny/profile /user/johnny/posts
-+------------------+ +-----------------+
-| User | | User |
-| +--------------+ | | +-------------+ |
-| | Profile | | +------------> | | Posts | |
-| | | | | | | |
-| +--------------+ | | +-------------+ |
-+------------------+ +-----------------+
+/user/johnny/profile /user/johnny/posts
+┌──────────────────┐ ┌──────────────────┐
+│ User │ │ User │
+│ ┌──────────────┐ │ │ ┌──────────────┐ │
+│ │ Profile │ │ ●────────────▶ │ │ Posts │ │
+│ │ │ │ │ │ │ │
+│ └──────────────┘ │ │ └──────────────┘ │
+└──────────────────┘ └──────────────────┘
```
通过 Vue Router,你可以使用嵌套路由配置来表达这种关系。
接着上节创建的 app :
-```html
-<div id="app">
- <router-view></router-view>
-</div>
+```vue
+<!-- App.vue -->
+<template>
+ <router-view />
+</template>
+```
+
+```vue
+<!-- User.vue -->
+<template>
+ <div>
+ User {{ $route.params.id }}
+ </div>
+</template>
```
```js
-const User = {
- template: '<div>User {{ $route.params.id }}</div>',
-}
+import User from './User.vue'
// 这些都会传递给 `createRouter`
const routes = [{ path: '/user/:id', component: User }]
这里的 `<router-view>` 是一个顶层的 `router-view`。它渲染顶层路由匹配的组件。同样地,一个被渲染的组件也可以包含自己嵌套的 `<router-view>`。例如,如果我们在 `User` 组件的模板内添加一个 `<router-view>`:
-```js
-const User = {
- template: `
- <div class="user">
- <h2>User {{ $route.params.id }}</h2>
- <router-view></router-view>
- </div>
- `,
-}
+```vue
+<!-- User.vue -->
+<template>
+ <div class="user">
+ <h2>User {{ $route.params.id }}</h2>
+ <router-view />
+ </div>
+</template>
```
要将组件渲染到这个嵌套的 `router-view` 中,我们需要在路由中配置 `children`:
},
]
```
+
+<!-- TODO: translation -->
+## Omitting parent components <Badge text="4.1+" />
+
+We can also take advantage of the parent-child relationship between routes without needing to nest route components. This can be useful for grouping together routes with a common path prefix, or when working with more advanced features, such as [per-route navigation guards](../advanced/navigation-guards#Per-Route-Guard) or [route meta fields](../advanced/meta).
+
+To achieve this, we omit the `component` and `components` options from the parent route:
+
+```js
+const routes = [
+ {
+ path: '/admin',
+ children: [
+ { path: '', component: AdminOverview },
+ { path: 'users', component: AdminUserList },
+ { path: 'users/:id', component: AdminUserDetails },
+ ],
+ },
+]
+```
+
+As the parent doesn't specify a route component, the top-level `<router-view>` will skip over the parent and just use the component from the relevant child instead.
title="Learn how to pass props to route components"
/>
-在你的组件中使用 `$route` 会与路由紧密耦合,这限制了组件的灵活性,因为它只能用于特定的 URL。虽然这不一定是件坏事,但我们可以通过 `props` 配置来解除这种行为:
+在你的组件中使用 `$route` 或 `useRoute()` 会与路由紧密耦合,这限制了组件的灵活性,因为它只能用于特定的 URL。虽然这不一定是件坏事,但我们可以通过 `props` 配置来解除这种行为:
-我们可以将下面的代码
+回到我们之前的示例:
+
+```vue
+<!-- User.vue -->
+<template>
+ <div>
+ User {{ $route.params.id }}
+ </div>
+</template>
+```
+
+和:
```js
-const User = {
- template: '<div>User {{ $route.params.id }}</div>'
+import User from './User.vue'
+
+// 传入 `createRouter`
+const routes = [
+ { path: '/users/:id', component: User },
+]
+```
+
+我们可以通过声明 prop 来在 `User.vue` 中删除对 `$route` 的直接依赖:
+
+::: code-group
+
+```vue [Composition API]
+<!-- User.vue -->
+<script setup>
+defineProps({
+ id: String
+})
+</script>
+
+<template>
+ <div>
+ User {{ id }}
+ </div>
+</template>
+```
+
+```vue [Options API]
+<!-- User.vue -->
+<script>
+export default {
+ props: {
+ id: String
+ }
}
-const routes = [{ path: '/user/:id', component: User }]
+</script>
+
+<template>
+ <div>
+ User {{ id }}
+ </div>
+</template>
```
-替换成
+:::
+
+然后我们可以通过设置 `props: true` 来配置路由将 `id` 参数作为 prop 传递给组件:
```js
-const User = {
- // 请确保添加一个与路由参数完全相同的 prop 名
- props: ['id'],
- template: '<div>User {{ id }}</div>'
-}
-const routes = [{ path: '/user/:id', component: User, props: true }]
+const routes = [
+ { path: '/user/:id', component: User, props: true }
+]
```
这允许你在任何地方使用该组件,使得该组件更容易重用和测试。
# 安装
-## 直接下载 / CDN
-
-[https://unpkg.com/vue-router@4](https://unpkg.com/vue-router@4)
-
-<!--email_off-->
-
-[Unpkg.com](https://unpkg.com) 提供了基于 npm 的 CDN 链接。上述链接将始终指向 npm 上的最新版本。 你也可以通过像 `https://unpkg.com/vue-router@4.0.15/dist/vue-router.global.js` 这样的 URL 来使用特定的版本或 Tag。
-
-<!--/email_off-->
-
-这将把 Vue Router 暴露在一个全局的 `VueRouter` 对象上,例如 `VueRouter.createRouter(...)`。
+<VueMasteryLogoLink></VueMasteryLogoLink>
## 包管理器
你需要回答一些关于你想创建的项目类型的问题。如果您选择安装 Vue Router,示例应用还将演示 Vue Router 的一些核心特性。
使用包管理器的项目通常会使用 ES 模块来访问 Vue Router,例如 `import { createRouter } from 'vue-router'`。
+
+## 直接下载 / CDN
+
+[https://unpkg.com/vue-router@4](https://unpkg.com/vue-router@4)
+
+<!--email_off-->
+
+[Unpkg.com](https://unpkg.com) 提供了基于 npm 的 CDN 链接。上述链接将始终指向 npm 上的最新版本。 你也可以通过像 `https://unpkg.com/vue-router@4.0.15/dist/vue-router.global.js` 这样的 URL 来使用特定的版本或 Tag。
+
+<!--/email_off-->
+
+这将把 Vue Router 暴露在一个全局的 `VueRouter` 对象上,例如 `VueRouter.createRouter(...)`。
+