|
1 | 1 | # 入门
|
2 | 2 |
|
3 | 3 | <VueSchoolLink
|
4 |
| - href="https://vueschool.io/courses/vue-router-4-for-everyone" |
5 |
| - title="Learn how to build powerful Single Page Applications with the Vue Router on Vue School" |
6 |
| -/> |
| 4 | +href="https://vueschool.io/courses/vue-router-4-for-everyone" |
| 5 | +title="在 Vue School 上学习如何使用 Vue Router 构建强大的单页应用">观看免费的 Vue Router 视频课程</VueSchoolLink> |
7 | 6 |
|
8 |
| -用 Vue + Vue Router 创建单页应用非常简单:通过 Vue.js,我们已经用组件组成了我们的应用。当加入 Vue Router 时,我们需要做的就是将我们的组件映射到路由上,让 Vue Router 知道在哪里渲染它们。下面是一个基本的例子: |
| 7 | +Vue Router 是 Vue 官方的客户端路由解决方案。 |
9 | 8 |
|
10 |
| -## HTML |
| 9 | +客户端路由的作用是在单页应用 (SPA) 中将浏览器的 URL 和用户看到的内容绑定起来。当用户在应用中浏览不同页面时,URL 会随之更新,但页面不需要从服务器重新加载。 |
11 | 10 |
|
12 |
| -```html |
13 |
| -<script src="https://unpkg.com/vue@3"></script> |
14 |
| -<script src="https://unpkg.com/vue-router@4"></script> |
| 11 | +Vue Router 基于 Vue 的组件系统构建,你可以通过配置**路由**来告诉 Vue Router 为每个 URL 路径显示哪些组件。 |
15 | 12 |
|
16 |
| -<div id="app"> |
| 13 | +::: tip 学习基础 |
| 14 | +这份指南假设你已经对 Vue 有了一定的了解。你不必是 Vue 的专家,但你也许偶尔需要查看 [Vue 的文档](https://cn.vuejs.org/)来了解某些特性。 |
| 15 | +::: |
| 16 | + |
| 17 | +## 示例 |
| 18 | + |
| 19 | +为了引入一些核心概念,我们将使用如下的示例: |
| 20 | + |
| 21 | +- [Vue 演练场中的示例](https://play.vuejs.org/#eNqFVVtv2zYU/itn6gArmC05btEHTXXTFcWyYZeiLfYy7UGWji02EsmRlOPA8H/fIambnaRD4Fg61++c7yN9DJqc8eirDpKANVIoA0coFOYG30kJJ9gq0cBs3+Is412AEq1B1Xmi2L+ObpvX+3IpI5+b8aFqSJ+rjANErcbQp/v3RrTchLMXlDa7CuZBl07YUoONrCl/bQPT6np9i3UtbLPv0phenVm6L3rQRgm+W79vlULeIQaZmypJ484HxyN87xzRtq3rj+SE08mViX2dlOf7vuAnh/I3xu/AiDdZEGfB+mdBz3ArGkzj0f9sRr4hy5D2zr49ykvjvmdqeTmv9RfDe4i7uM6dxsNiaF9+l0+y+Ts2Qj3cMm3oa94Zfd0py4uBzYFPO6Br3ZPaGzpme9rtQGdxg2WUgOC6Y0PDG/jbjnL0vMAsnhEsQcU4UZaMbU/z8zC3x/PYsbcN/ueilaJW03nDoy1Y+VUkT+0nvHI9PVB6PJE8M44HN2iJ27yt+9q09ek+rFR1oZg0RM5FgmvboKlEqRP/BrATX4SDH171JgBD4CIvThXJVldhP7Y7J9DtxP4nxZKk+470cnFQVuseHh2TlTduWmMEh5uiZsUdSXPAcKlOH/hIZmfEjhODRtPaozNKjyiiGcqn75Ej0Pl3lMyHp2fFeMHnEB/SRia+ict6ep/GXBWV1UGHyGtgh5O1K0KvuC8T/duieoi6tLdvYUYg+rXTmKH3jLmeKoW0owLDI7h8IrnvfAKrIargxfQ/lA0LHjmr8w3W3X3w2dVMIGWchoH9ohEl1pFRrCE2fccsgCY/1Mh3piLjaknc+pujr3TOqedk0eSSrg/BiVU3WtY5dBYMks2CkRtrzoLKGKmTOG65vNtFtON4jLh5Fb2MlnFJJ2tijVA3i40S99rdV1ngNmtr31BQXOLeCFHrRS7Zcy0eBd68jl5H13HNNjFVjxkv8eBq94unMY0mQWzZ7mJIKwtWo/pTGkaCORs2p9+Z+1+dzagWB6BFhcXdE/av+uAhf1RI0+1xMpzJFWnOuz98/gMP9Dw4icW2puhvOD+hFnVrMfqwn1peEuxJnEP7i+OM8d0X/eFgkOt+KAt0FLIj8v03Rh/hvoxeTbaozUONOiq0/aGhX6w5aY1xn7cRqkSVwEoegMCyEl4sl8sf3d1H5RhfbATdKk0C10t5cHaZlyWBHSzUJeNUFtaQww/08Tenz65xSzf+NLJaTTuP5UcARVFMACSwpL9VVyE4/QesCg/V) |
| 22 | + |
| 23 | +让我们首先来看根组件, `App.vue`。 |
| 24 | + |
| 25 | +### App.vue |
| 26 | + |
| 27 | +```vue |
| 28 | +<template> |
17 | 29 | <h1>Hello App!</h1>
|
18 | 30 | <p>
|
19 |
| - <!-- 使用 router-link 组件进行导航 --> |
20 |
| - <!-- 通过传递 `to` 来指定链接 --> |
21 |
| - <!-- `<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签 --> |
22 |
| - <router-link to="/">Go to Home</router-link> |
23 |
| - <router-link to="/about">Go to About</router-link> |
| 31 | + <strong>Current route path:</strong> {{ $route.fullPath }} |
24 | 32 | </p>
|
25 |
| - <!-- 路由出口 --> |
26 |
| - <!-- 路由匹配到的组件将渲染在这里 --> |
27 |
| - <router-view></router-view> |
28 |
| -</div> |
| 33 | + <nav> |
| 34 | + <RouterLink to="/">Go to Home</RouterLink> |
| 35 | + <RouterLink to="/about">Go to About</RouterLink> |
| 36 | + </nav> |
| 37 | + <main> |
| 38 | + <RouterView /> |
| 39 | + </main> |
| 40 | +</template> |
29 | 41 | ```
|
30 | 42 |
|
31 |
| -### `router-link` |
| 43 | +在这个 `template` 中使用了两个由 Vue Router 提供的组件: `RouterLink` 和 `RouterView`。 |
| 44 | + |
| 45 | +不同于常规的 `<a>` 标签,我们使用组件 `RouterLink` 来创建链接。这使得 Vue Router 能够在不重新加载页面的情况下改变 URL,处理 URL 的生成、编码和其他功能。我们将会在之后的部分深入了解 `RouterLink` 组件。 |
32 | 46 |
|
33 |
| -请注意,我们没有使用常规的 `a` 标签,而是使用一个自定义组件 `router-link` 来创建链接。这使得 Vue Router 可以在不重新加载页面的情况下更改 URL,处理 URL 的生成以及编码。我们将在后面看到如何从这些功能中获益。 |
| 47 | +`RouterView` 组件可以使 Vue Router 知道你想要在哪里渲染当前 URL 路径对应的**路由组件**。它不一定要在 `App.vue` 中,你可以把它放在任何地方,但它需要在某处被导入,否则 Vue Router 就不会渲染任何东西。 |
34 | 48 |
|
35 |
| -### `router-view` |
| 49 | +上述示例还使用了 <code v-pre>{{ $route.fullPath }}</code> 。你可以在组件模板中使用 `$route` 来访问当前的路由对象。 |
36 | 50 |
|
37 |
| -`router-view` 将显示与 URL 对应的组件。你可以把它放在任何地方,以适应你的布局。 |
| 51 | +<VueMasteryLogoLink></VueMasteryLogoLink> |
38 | 52 |
|
39 |
| -## JavaScript |
| 53 | +### 创建路由器实例 |
| 54 | + |
| 55 | +路由器实例是通过调用 `createRouter()` 函数创建的: |
40 | 56 |
|
41 | 57 | ```js
|
42 |
| -// 1. 定义路由组件. |
43 |
| -// 也可以从其他文件导入 |
44 |
| -const Home = { template: '<div>Home</div>' } |
45 |
| -const About = { template: '<div>About</div>' } |
46 |
| - |
47 |
| -// 2. 定义一些路由 |
48 |
| -// 每个路由都需要映射到一个组件。 |
49 |
| -// 我们后面再讨论嵌套路由。 |
| 58 | +import { createMemoryHistory, createRouter } from 'vue-router' |
| 59 | + |
| 60 | +import HomeView from './HomeView.vue' |
| 61 | +import AboutView from './AboutView.vue' |
| 62 | + |
50 | 63 | const routes = [
|
51 |
| - { path: '/', component: Home }, |
52 |
| - { path: '/about', component: About }, |
| 64 | + { path: '/', component: HomeView }, |
| 65 | + { path: '/about', component: AboutView }, |
53 | 66 | ]
|
54 | 67 |
|
55 |
| -// 3. 创建路由实例并传递 `routes` 配置 |
56 |
| -// 你可以在这里输入更多的配置,但我们在这里 |
57 |
| -// 暂时保持简单 |
58 |
| -const router = VueRouter.createRouter({ |
59 |
| - // 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。 |
60 |
| - history: VueRouter.createWebHashHistory(), |
61 |
| - routes, // `routes: routes` 的缩写 |
| 68 | +const router = createRouter({ |
| 69 | + history: createMemoryHistory(), |
| 70 | + routes, |
62 | 71 | })
|
| 72 | +``` |
63 | 73 |
|
64 |
| -// 5. 创建并挂载根实例 |
65 |
| -const app = Vue.createApp({}) |
66 |
| -// 确保 _use_ 路由实例使 |
67 |
| -// 整个应用支持路由。 |
68 |
| -app.use(router) |
| 74 | +这里的 `routes` 选项定义了一组路由,把 URL 路径映射到组件。其中,由 `component` 参数指定的组件就是先前在 `App.vue` 中被 `<RouterView>` 渲染的组件。这些路由组件通常被称为*视图*,但本质上它们只是普通的 Vue 组件。 |
69 | 75 |
|
70 |
| -app.mount('#app') |
| 76 | +其他可以设置的路由选项我们会在之后介绍,目前我们只需要 `path` 和 `component`。 |
| 77 | + |
| 78 | +这里的 `history` 选项控制了路由和 URL 路径是如何双向映射的。在演练场的示例里,我们使用了 `createMemoryHistory()`,它会完全忽略浏览器的 URL 而使用其自己内部的 URL。 这在演练场中可以正常工作,但是未必是你想要在实际应用中使用的。通常,你应该使用 `createWebHistory()` 或 `createWebHashHistory()`。我们将在[不同的历史记录模式](./essentials/history-mode)的部分详细介绍这个主题。 |
| 79 | + |
| 80 | +### 注册路由器插件 |
| 81 | + |
| 82 | +一旦创建了我们的路由器实例,我们就需要将其注册为插件,这一步骤可以通过调用 `use()` 来完成。 |
| 83 | + |
| 84 | +```js |
| 85 | +createApp(App) |
| 86 | + .use(router) |
| 87 | + .mount('#app') |
| 88 | +``` |
| 89 | + |
| 90 | +或等价地: |
71 | 91 |
|
72 |
| -// 现在,应用已经启动了! |
| 92 | +```js |
| 93 | +const app = createApp(App) |
| 94 | +app.use(router) |
| 95 | +app.mount('#app') |
73 | 96 | ```
|
74 | 97 |
|
75 |
| -通过调用 `app.use(router)`,我们会触发第一次导航且可以在任意组件中以 `this.$router` 的形式访问它,并且以 `this.$route` 的形式访问当前路由: |
| 98 | +和大多数的 Vue 插件一样,`use()` 需要在 `mount()` 之前调用。 |
| 99 | + |
| 100 | +如果你好奇这个插件做了什么,它的职责包括: |
| 101 | + |
| 102 | +1. [全局注册](https://cn.vuejs.org/guide/components/registration.html#global-registration) `RouterView` 和 `RouterLink` 组件。 |
| 103 | +2. 添加全局 `$router` 和 `$route` 属性。 |
| 104 | +3. 启用 `useRouter()` 和 `useRoute()` 组合式函数。 |
| 105 | +4. 触发路由器解析初始路由。 |
| 106 | + |
| 107 | +### 访问路由器和当前路由 |
| 108 | + |
| 109 | +你很可能想要在应用的其他地方访问路由器。 |
| 110 | + |
| 111 | +如果你是从 ES 模块导出路由器实例的,你可以将路由器实例直接导入到你需要它的地方。在一些情况下这是最好的方法,但如果我们在组件内部,那么我们还有其他选择。 |
| 112 | + |
| 113 | +在组件模板中,路由器实例将被暴露为 `$router`。这与同样被暴露的 `$route` 一样,但注意前者最后有一个额外的 `r`。 |
| 114 | + |
| 115 | +如果我们使用选项式 API,我们可以在 JavaScript 中如下访问这两个属性:`this.$router` 和 `this.$route`。在演练场示例中的 `HomeView.vue` 组件中,路由器就是这样获取的。 |
76 | 116 |
|
77 | 117 | ```js
|
78 |
| -// Home.vue |
79 | 118 | export default {
|
80 |
| - computed: { |
81 |
| - username() { |
82 |
| - // 我们很快就会看到 `params` 是什么 |
83 |
| - return this.$route.params.username |
84 |
| - }, |
85 |
| - }, |
86 | 119 | methods: {
|
87 |
| - goToDashboard() { |
88 |
| - if (isAuthenticated) { |
89 |
| - this.$router.push('/dashboard') |
90 |
| - } else { |
91 |
| - this.$router.push('/login') |
92 |
| - } |
| 120 | + goToAbout() { |
| 121 | + this.$router.push('/about') |
93 | 122 | },
|
94 | 123 | },
|
95 | 124 | }
|
96 | 125 | ```
|
97 | 126 |
|
98 |
| -要在 `setup` 函数中访问路由,请调用 `useRouter` 或 `useRoute` 函数。我们将在 [Composition API](./advanced/composition-api.md#在-setup-中访问路由和当前路由) 中了解更多信息。 |
| 127 | +这里调用了 `push()`,这是用于[编程式导航](./essentials/navigation)的方法。我们会在后面详细了解。 |
| 128 | + |
| 129 | + |
| 130 | +对于组合式 API,我们不能通过 `this` 访问组件实例,所以 Vue Router 给我们提供了一些组合式函数。演练场示例中的 `AboutView.vue` 组件使用了这种方法: |
| 131 | + |
| 132 | +```vue |
| 133 | +<script setup> |
| 134 | +import { computed } from 'vue' |
| 135 | +import { useRoute, useRouter } from 'vue-router' |
| 136 | +
|
| 137 | +const router = useRouter() |
| 138 | +const route = useRoute() |
| 139 | +
|
| 140 | +const search = computed({ |
| 141 | + get() { |
| 142 | + return route.query.search ?? '' |
| 143 | + }, |
| 144 | + set(search) { |
| 145 | + router.replace({ query: { search } }) |
| 146 | + } |
| 147 | +}) |
| 148 | +</script> |
| 149 | +``` |
| 150 | + |
| 151 | +你现在不一定要完全理解这段代码,关键是要知道可以通过 `useRoute()` 和 `useRouter()` 来访问路由器实例和当前路由。 |
| 152 | + |
| 153 | +### 下一步 |
| 154 | + |
| 155 | +如果你想要在 Vite 中使用完整的示例,你可以使用 [create-vue](https://github.com/vuejs/create-vue) 工具,它提供了在项目加入 Vue Router 的选项: |
| 156 | + |
| 157 | +::: code-group |
| 158 | + |
| 159 | +```bash [npm] |
| 160 | +npm create vue@latest |
| 161 | +``` |
| 162 | + |
| 163 | +```bash [yarn] |
| 164 | +yarn create vue |
| 165 | +``` |
| 166 | + |
| 167 | +```bash [pnpm] |
| 168 | +pnpm create vue |
| 169 | +``` |
| 170 | + |
| 171 | +::: |
| 172 | + |
| 173 | +上述通过 create-vue 创建的示例项目使用了与我们在这里看到的类似的功能,对于探索后续介绍的其他功能而言,也许你会觉得这是一个不错的起点。 |
| 174 | + |
| 175 | +## 本教程的约定 |
| 176 | + |
| 177 | +### 单文件组件 |
| 178 | + |
| 179 | +Vue Router 经常在配合打包工具 (如 Vite) 和[单文件组件](https://cn.vuejs.org/guide/introduction.html#single-file-components) (即 `.vue` 文件) 的应用中使用。本教程大多数的示例都是如此,但是 Vue Router 本身并不要求你使用构建工具或单文件组件。 |
| 180 | + |
| 181 | +例如,若你要使用 [Vue](https://cn.vuejs.org/guide/quick-start.html#using-vue-from-cdn) 和 [Vue Router](../installation#Direct-Download-CDN) 的使用全局构建版本,这些库将被暴露为全局对象,而不是导入: |
| 182 | + |
| 183 | +```js |
| 184 | +const { createApp } = Vue |
| 185 | +const { createRouter, createWebHistory } = VueRouter |
| 186 | +``` |
| 187 | + |
| 188 | +### 组件 API 风格 |
| 189 | + |
| 190 | +Vue Router 可以使用组合式 API 或选项式 API 。在必要时,示例将会同时使用两种风格,组合式 API 示例通常会使用 `<script setup>`,而不是显式的 `setup` 函数。 |
| 191 | + |
| 192 | +如果你对于这两种风格不熟悉,可以参考 [Vue - API 风格](https://cn.vuejs.org/guide/introduction.html#api-styles)。 |
| 193 | + |
| 194 | +### `router` 和 `route` |
| 195 | + |
| 196 | +在本教程中,我们常常以 `router` 作为路由器实例提及。即由 `createRouter()` 返回的对象。在应用中,访问该对象的方式取决于上下文。例如,在组合式 API 中,它可以通过调用 `useRouter()` 来访问。在选项式 API 中,它可以通过 `this.$router` 来访问。 |
| 197 | + |
| 198 | +类似地,当前路由会以 `route` 被提及。基于不同 API 风格的组件,它可以通过 `useRoute()` 或 `this.$route` 来访问。 |
| 199 | + |
| 200 | +### `RouterView` 和 `RouterLink` |
| 201 | + |
| 202 | +组件 `RouterView` 和 `RouterLink` 都是[全局注册](https://cn.vuejs.org/guide/components/registration.html#global-registration)的,因此它们不需要在组件模板中导入。但你也可以通过局部导入它们,例如 `import { RouterLink } from 'vue-router'`。 |
| 203 | + |
| 204 | +在模板中,组件的名字可以是 PascalCase 风格或 kebab-case 风格的。Vue 的模板编译器支持两种格式,因此 `<RouterView>` 和 `<router-view>` 通常是等效的。此时应该遵循你自己项目中使用的约定。 |
99 | 205 |
|
100 |
| -在整个文档中,我们会经常使用 `router` 实例,请记住,`this.$router` 与直接使用通过 `createRouter` 创建的 `router` 实例完全相同。我们使用 `this.$router` 的原因是,我们不想在每个需要操作路由的组件中都导入路由。 |
| 206 | +如果使用 DOM 内模板,那么需要[注意](https://cn.vuejs.org/guide/essentials/component-basics.html#in-dom-template-parsing-caveats):组件名字必须使用 kebab-case 风格且不支持自闭合标签。因此你不能直接写 `<RouterView />`,而需要使用 `<router-view></router-view>`。 |
0 commit comments