]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
docs(zh): sync to d842b6f (#2244)
authorJinjiang <zhaojinjiang@me.com>
Tue, 11 Jun 2024 03:20:40 +0000 (11:20 +0800)
committerGitHub <noreply@github.com>
Tue, 11 Jun 2024 03:20:40 +0000 (11:20 +0800)
* docs(zh): sync to d842b6f

* docs(zh): translate the new docs

* docs(zh): update the checkpoint

* Update packages/docs/zh/guide/essentials/named-routes.md

Co-authored-by: GU Yiling <justice360@gmail.com>
* Apply suggestions from code review

Co-authored-by: GU Yiling <justice360@gmail.com>
* Update zh.ts

* Update data-fetching.md

* Update composition-api.md

* Update navigation-guards.md

* Update navigation-guards.md

---------

Co-authored-by: GU Yiling <justice360@gmail.com>
16 files changed:
packages/docs/.vitepress/config/zh.ts
packages/docs/.vitepress/translation-status.json
packages/docs/zh/guide/advanced/composition-api.md
packages/docs/zh/guide/advanced/data-fetching.md
packages/docs/zh/guide/advanced/dynamic-routing.md
packages/docs/zh/guide/advanced/extending-router-link.md
packages/docs/zh/guide/advanced/meta.md
packages/docs/zh/guide/advanced/navigation-failures.md
packages/docs/zh/guide/advanced/navigation-guards.md
packages/docs/zh/guide/essentials/active-links.md [new file with mode: 0644]
packages/docs/zh/guide/essentials/dynamic-matching.md
packages/docs/zh/guide/essentials/named-routes.md
packages/docs/zh/guide/essentials/named-views.md
packages/docs/zh/guide/essentials/nested-routes.md
packages/docs/zh/guide/essentials/passing-props.md
packages/docs/zh/installation.md

index 7613a8b19705bacb61f2f125e941a97f1e175b54..dbc6ba4abfbdb8a0c14e7b5a4e6cc0713c915f81 100644 (file)
@@ -57,15 +57,9 @@ export const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
     ],
 
     sidebar: {
-      '/zh/api/': [
-        {
-          text: 'packages',
-          items: [{ text: 'vue-router', link: '/zh/api/' }],
-        },
-      ],
-
       '/zh/': [
         {
+          text: '设置',
           items: [
             {
               text: '介绍',
@@ -96,14 +90,14 @@ export const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
               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',
@@ -116,6 +110,10 @@ export const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
               text: '路由组件传参',
               link: '/zh/guide/essentials/passing-props.html',
             },
+            {
+              text: '匹配当前路由的链接',
+              link: '/zh/guide/essentials/active-links.html',
+            },
             {
               text: '不同的历史记录模式',
               link: '/zh/guide/essentials/history-mode.html',
@@ -188,6 +186,13 @@ export const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
           ],
         },
       ],
+
+      '/zh/api/': [
+        {
+          text: 'packages',
+          items: [{ text: 'vue-router', link: '/zh/api/' }],
+        },
+      ],
     },
   },
 }
index ea46a91271b857ebdc9397032ba53039f27abf4c..85dfe91229616a6a49e55a7f1fb986b5d49a8cdf 100644 (file)
@@ -1,6 +1,6 @@
 {
   "zh": {
-    "hash": "35a9c1b",
-    "date": "2023-12-16"
+    "hash": "d842b6f",
+    "date": "2024-05-17"
   }
 }
\ No newline at end of file
index da2e9b82144c09942a74a52f05969296b58906d5..2385633e9d727918c495b1d0aec6a230408065a5 100644 (file)
@@ -5,87 +5,81 @@
   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>` 渲染的组件中,它们不必像组件内守卫那样直接用在路由组件上。
@@ -94,40 +88,34 @@ export default {
 
 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` 组合式函数相同的属性。
index 521a6396f504ec58d0db8c82dd6ca35eea87dc53..4355a30d33f0383deedaf4b4faf7e68c1eda0a16 100644 (file)
 
 ## 导航完成后获取数据
 
-当你使用这种方式时,我们会马上导航和渲染组件,然后在组件的 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 {
@@ -39,35 +86,36 @@ export default {
     }
   },
   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` 方法:
@@ -81,28 +129,27 @@ export default {
     }
   },
   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()
     }
   }
 }
index 380b7f22afe7b0daf21db4640f12925995fbdfdd..ba7b602d361065798137eace9cda49549beff972 100644 (file)
@@ -30,7 +30,7 @@ router.addRoute({ path: '/about', component: About })
 
 ```js
 router.addRoute({ path: '/about', component: About })
-// 我们也可以使用 this.$route 或 route = useRoute() (在 setup 中)
+// 我们也可以使用 this.$route 或 useRoute()
 router.replace(router.currentRoute.value.fullPath)
 ```
 
index 5723024475808bf966a8ce2f8cdd87ba1687818e..16a21996c31ddba2b56e1246013e1ca7b2e69d34 100644 (file)
@@ -9,7 +9,28 @@ RouterLink 组件提供了足够的 `props` 来满足大多数基本应用程序
 
 让我们扩展 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 />
@@ -30,7 +51,9 @@ RouterLink 组件提供了足够的 `props` 来满足大多数基本应用程序
     </a>
   </router-link>
 </template>
+```
 
+```vue [Options API]
 <script>
 import { RouterLink } from 'vue-router'
 
@@ -51,8 +74,31 @@ export default {
   },
 }
 </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
index 939d5df09cbb91908c0f6c2fb070d957e03715fe..1063e01f9846bbc2702f525943fb8a282386f2af 100644 (file)
@@ -38,7 +38,7 @@ const routes = [
 
 例如,根据上面的路由配置,`/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) => {
index fccc55941ddff1112e40913155f101bbe9325e84..79460435d43555b5372c73c3b5103bf4df7d4897 100644 (file)
@@ -62,8 +62,6 @@ if (isNavigationFailure(failure, NavigationFailureType.aborted)) {
 如果你忽略第二个参数: `isNavigationFailure(failure)`,那么就只会检查这个 `failure` 是不是一个 _Navigation Failure_。
 :::
 
-<!-- TODO: translation -->
-
 ## 全局导航故障
 
 你可以用 [`router.afterEach()` 导航守卫](./navigation-guards.md#Global-After-Hooks)检测全局导航故障:
index 7952384c352b1138c7d64408be2a9da853811c8c..e84e6037be103c829ef036f5266ec6a4bd86d748 100644 (file)
@@ -86,7 +86,7 @@ router.beforeEach((to, from, next) => {
 
 ## 全局解析守卫
 
-你可以用 `router.beforeResolve` 注册一个全局守卫。这和 `router.beforeEach` 类似,因为它在**每次导航**时都会触发,不同的是,解析守卫刚好会在导航被确认之前、**所有组件内守卫和异步路由组件被解析之后**调用。这里有一个例子,确保用户可以访问[自定义 meta](./meta.md) 属性 `requiresCamera` 的路由
+你可以用 `router.beforeResolve` 注册一个全局守卫。这和 `router.beforeEach` 类似,因为它在**每次导航**时都会触发,不同的是,解析守卫刚好会在导航被确认之前、**所有组件内守卫和异步路由组件被解析之后**调用。这里有一个例子,根据路由在[元信息](./meta.md)中的 `requiresCamera` 属性确保用户访问摄像头的权限
 
 ```js
 router.beforeResolve(async to => {
@@ -198,7 +198,28 @@ const routes = [
 ]
 ```
 
-请注意,你也可以通过使用[路径 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)和全局导航守卫来实现类似的行为。
+:::
 
 ## 组件内的守卫
 
@@ -212,9 +233,9 @@ const routes = [
 - `beforeRouteUpdate`
 - `beforeRouteLeave`
 
-```js
-const UserDetails = {
-  template: `...`,
+```vue
+<script>
+export default {
   beforeRouteEnter(to, from) {
     // 在渲染该组件的对应路由被验证前调用
     // 不能获取组件实例 `this` !
@@ -231,6 +252,7 @@ const UserDetails = {
     // 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
   },
 }
+</script>
 ```
 
 `beforeRouteEnter` 守卫 **不能** 访问 `this`,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。
@@ -265,7 +287,7 @@ beforeRouteLeave (to, from) {
 
 ### 使用组合 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#导航守卫)以获得更多细节。
 
 ## 完整的导航解析流程
 
diff --git a/packages/docs/zh/guide/essentials/active-links.md b/packages/docs/zh/guide/essentials/active-links.md
new file mode 100644 (file)
index 0000000..76f93a4
--- /dev/null
@@ -0,0 +1,80 @@
+<!-- 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.
index 7d0646f1e84c8ccbe006e5725671dd5676656b5b..5ba892e86231676262db261f03a3099f8d5a9701 100644 (file)
@@ -8,9 +8,7 @@
 很多时候,我们需要将给定匹配模式的路由映射到同一个组件。例如,我们可能有一个 `User` 组件,它应该对所有用户进行渲染,但用户 ID 不同。在 Vue Router 中,我们可以在路径中使用一个动态字段来实现,我们称之为 _路径参数_ :
 
 ```js
-const User = {
-  template: '<div>User</div>',
-}
+import User from './User.vue'
 
 // 这些都会传递给 `createRouter`
 const routes = [
@@ -21,22 +19,25 @@ 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)找到。
 
@@ -59,32 +60,68 @@ const User = {
 
 要对同一个组件中参数的变化做出响应的话,你可以简单地 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
@@ -96,9 +133,9 @@ const User = {
 
 ```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 },
 ]
 ```
@@ -106,13 +143,13 @@ const routes = [
 在这个特定的场景中,我们在括号之间使用了[自定义正则表达式](./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,
 })
 ```
 
index 9bd05c4e64227885da4d02ff3a376376dd839f28..da660ff8542612c1703d99d94099b92a2b5cfe52 100644 (file)
@@ -5,39 +5,41 @@
   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' } })
+```
index 11e8f5f180b84f4d5af7666c4a0c8a645bd52ff6..b9c568a9d2b0d00c48f702f471420f8ae8b8e697 100644 (file)
@@ -7,10 +7,10 @@
 
 有时候想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有 `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**):
index 56e4fd2ae601f011db98b225b477547170d54280..7984ae5e024477bbd8e0bc22249dec944cb3d2cc 100644 (file)
@@ -8,30 +8,38 @@
 一些应用程序的 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 }]
@@ -39,15 +47,14 @@ 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`:
@@ -128,3 +135,25 @@ const routes = [
   },
 ]
 ```
+
+<!-- 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.
index 1b189ca755a0632bfbc3cc565595572fafdd914d..dab41339fed66f68c123fc9c658e763e2faf3676 100644 (file)
@@ -5,26 +5,74 @@
   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 }
+]
 ```
 
 这允许你在任何地方使用该组件,使得该组件更容易重用和测试。
index e7fb952138e0ae141a84c05509cacdf5a451d3e7..5c6c45f7f56c28b506fff40e9a0a2620f0e5ab0a 100644 (file)
@@ -1,16 +1,6 @@
 # 安装
 
-## 直接下载 / 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>
 
 ## 包管理器
 
@@ -53,3 +43,16 @@ pnpm create vue
 你需要回答一些关于你想创建的项目类型的问题。如果您选择安装 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(...)`。
+