]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
docs: add chinese docs
authorEduardo San Martin Morote <posva13@gmail.com>
Thu, 26 Jan 2023 16:50:51 +0000 (17:50 +0100)
committerEduardo San Martin Morote <posva@users.noreply.github.com>
Wed, 15 Feb 2023 08:52:45 +0000 (09:52 +0100)
30 files changed:
packages/docs/.vitepress/config/en.ts
packages/docs/.vitepress/config/index.ts
packages/docs/.vitepress/config/shared.ts
packages/docs/.vitepress/config/zh.ts
packages/docs/.vitepress/theme/styles/sponsors.css
packages/docs/zh/api/index.md [new file with mode: 0644]
packages/docs/zh/guide/advanced/composition-api.md [new file with mode: 0644]
packages/docs/zh/guide/advanced/data-fetching.md [new file with mode: 0644]
packages/docs/zh/guide/advanced/dynamic-routing.md [new file with mode: 0644]
packages/docs/zh/guide/advanced/extending-router-link.md [new file with mode: 0644]
packages/docs/zh/guide/advanced/lazy-loading.md [new file with mode: 0644]
packages/docs/zh/guide/advanced/meta.md [new file with mode: 0644]
packages/docs/zh/guide/advanced/navigation-failures.md [new file with mode: 0644]
packages/docs/zh/guide/advanced/navigation-guards.md [new file with mode: 0644]
packages/docs/zh/guide/advanced/scroll-behavior.md [new file with mode: 0644]
packages/docs/zh/guide/advanced/transitions.md [new file with mode: 0644]
packages/docs/zh/guide/essentials/dynamic-matching.md [new file with mode: 0644]
packages/docs/zh/guide/essentials/history-mode.md [new file with mode: 0644]
packages/docs/zh/guide/essentials/named-routes.md [new file with mode: 0644]
packages/docs/zh/guide/essentials/named-views.md [new file with mode: 0644]
packages/docs/zh/guide/essentials/navigation.md [new file with mode: 0644]
packages/docs/zh/guide/essentials/nested-routes.md [new file with mode: 0644]
packages/docs/zh/guide/essentials/passing-props.md [new file with mode: 0644]
packages/docs/zh/guide/essentials/redirect-and-alias.md [new file with mode: 0644]
packages/docs/zh/guide/essentials/route-matching-syntax.md [new file with mode: 0644]
packages/docs/zh/guide/index.md [new file with mode: 0644]
packages/docs/zh/guide/migration/index.md [new file with mode: 0644]
packages/docs/zh/index.md [new file with mode: 0644]
packages/docs/zh/installation.md [new file with mode: 0644]
packages/docs/zh/introduction.md [new file with mode: 0644]

index 93b33512baee82f7539f2a2e0582eef6e2665374..d9ecc62de44fbf4fea958d1cf7901634c4f20152 100644 (file)
@@ -1,9 +1,8 @@
 import type { DefaultTheme, LocaleSpecificConfig } from 'vitepress'
 
-export const META_URL = 'https://pinia.vuejs.org'
-export const META_TITLE = 'Pinia 🍍'
-export const META_DESCRIPTION =
-  'Intuitive, type safe, light and flexible Store for Vue'
+export const META_URL = 'https://router.vuejs.org'
+export const META_TITLE = 'Vue Router'
+export const META_DESCRIPTION = 'The official Router for Vue.js'
 
 export const enConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
   description: META_DESCRIPTION,
@@ -17,7 +16,7 @@ export const enConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
 
   themeConfig: {
     editLink: {
-      pattern: 'https://github.com/vuejs/pinia/edit/v2/packages/docs/:path',
+      pattern: 'https://github.com/vuejs/router/edit/v2/packages/docs/:path',
       text: 'Suggest changes to this page',
     },
 
@@ -171,19 +170,12 @@ export const enConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
         },
       ],
 
-      // '/api/': [
-      //   {
-      //     text: 'packages',
-      //     items: [
-      //       { text: 'pinia', link: '/api/modules/pinia.html' },
-      //       { text: '@pinia/nuxt', link: '/api/modules/pinia_nuxt.html' },
-      //       {
-      //         text: '@pinia/testing',
-      //         link: '/api/modules/pinia_testing.html',
-      //       },
-      //     ],
-      //   },
-      // ],
+      '/api/': [
+        {
+          text: 'packages',
+          items: [{ text: 'vue-router', link: '/api/' }],
+        },
+      ],
     },
   },
 }
index 9928873573b927f1254f27c1a485ae4e1ec0acc7..0feb87abfee2083360d0b38eb38c01722b1c28b3 100644 (file)
@@ -8,6 +8,6 @@ export default defineConfig({
 
   locales: {
     root: { label: 'English', lang: 'en-US', link: '/', ...enConfig },
-    // zh: { label: '简体中文', lang: 'zh-CN', link: '/zh/', ...zhConfig },
+    zh: { label: '简体中文', lang: 'zh-CN', link: '/zh/', ...zhConfig },
   },
 })
index bdf6d17237a5bad5dba4e12fe4fdccbaf5f312fb..4d03803c660d74e6ea811bf452226587abef2f2b 100644 (file)
@@ -1,6 +1,8 @@
 import { defineConfig, HeadConfig } from 'vitepress'
 
-export const META_IMAGE = 'https://pinia.vuejs.org/social.png'
+// TODO:
+// export const META_IMAGE = 'https://router.vuejs.org/social.png'
+export const META_IMAGE = null
 export const isProduction =
   process.env.NETLIFY && process.env.CONTEXT === 'production'
 
@@ -59,13 +61,13 @@ export const sharedConfig = defineConfig({
         content: 'summary_large_image',
       },
     ],
-    [
-      'meta',
-      {
-        property: 'twitter:image',
-        content: META_IMAGE,
-      },
-    ],
+    // [
+    //   'meta',
+    //   {
+    //     property: 'twitter:image',
+    //     content: META_IMAGE,
+    //   },
+    // ],
 
     [
       'script',
index 60b62f8869b0cde2645036b6cc20d412ed53b5f5..201b5de304e302c95b7d84b8cc67ec26d8a70077 100644 (file)
@@ -1,10 +1,8 @@
 import type { DefaultTheme, LocaleSpecificConfig } from 'vitepress'
 
-export const META_URL = 'https://pinia.vuejs.org'
-export const META_TITLE = 'Pinia 🍍'
-export const META_DESCRIPTION = '值得你喜欢的 Vue Store'
-// TODO: translation of this
-// 'Intuitive, type safe, light and flexible Store for Vue'
+export const META_URL = 'https://router.vuejs.org'
+export const META_TITLE = 'Vue Router'
+export const META_DESCRIPTION = 'Vue.js 的官方路由'
 
 export const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
   description: META_DESCRIPTION,
@@ -18,127 +16,160 @@ export const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
 
   themeConfig: {
     editLink: {
-      pattern: 'https://github.com/vuejs/pinia/edit/v2/packages/docs/:path',
+      pattern: 'https://github.com/vuejs/router/edit/v2/packages/docs/:path',
       text: '对本页提出修改建议',
     },
 
     outlineTitle: '本页内容',
 
     nav: [
-      // { text: 'Config', link: '/config/' },
-      // { text: 'Plugins', link: '/plugins/' },
       {
-        text: '指南',
-        link: '/zh/core-concepts/',
-        activeMatch: '^/zh/core-concepts/',
+        text: '教程',
+        link: '/zh/guide/',
+        activeMatch: '^/zh/guide/',
+      },
+      {
+        text: 'API 参考',
+        link: '/zh/api/',
+        activeMatch: '^/zh/api/',
+      },
+      {
+        text: 'v4.x',
+        items: [{ text: 'v3.x', link: 'https://v3.router.vuejs.org/zh' }],
       },
-      { text: 'API', link: '/zh/api/', activeMatch: '^/zh/api/' },
-      { text: '手册', link: '/zh/cookbook/', activeMatch: '^/zh/cookbook/' },
       {
         text: '相关链接',
         items: [
           {
-            text: '论坛',
-            link: 'https://github.com/vuejs/pinia/discussions',
+            text: 'Discussions',
+            link: 'https://github.com/vuejs/router/discussions',
           },
           {
             text: '更新日志',
-            link: 'https://github.com/vuejs/pinia/blob/v2/packages/pinia/CHANGELOG.md',
+            link: 'https://github.com/vuejs/router/blob/main/packages/router/CHANGELOG.md',
           },
         ],
       },
     ],
+
     sidebar: {
       '/zh/api/': [
         {
           text: 'packages',
-          items: [
-            { text: 'pinia', link: '/zh/api/modules/pinia.html' },
-            { text: '@pinia/nuxt', link: '/zh/api/modules/pinia_nuxt.html' },
-            {
-              text: '@pinia/testing',
-              link: '/zh/api/modules/pinia_testing.html',
-            },
-          ],
+          items: [{ text: 'vue-router', link: '/api/' }],
         },
       ],
+
       '/zh/': [
         {
-          text: '介绍',
           items: [
             {
-              text: 'Pinia 是什么?',
+              text: '介绍',
               link: '/zh/introduction.html',
             },
             {
-              text: 'å¼\80å§\8b',
-              link: '/zh/getting-started.html',
+              text: 'å®\89è£\85',
+              link: '/zh/installation.html',
             },
           ],
         },
         {
-          text: '核心概念',
+          text: '基础',
+          collapsible: false,
           items: [
-            { text: '定义 Store', link: '/zh/core-concepts/' },
-            { text: 'State', link: '/zh/core-concepts/state.html' },
-            { text: 'Getter', link: '/zh/core-concepts/getters.html' },
-            { text: 'Action', link: '/zh/core-concepts/actions.html' },
-            { text: '插件', link: '/zh/core-concepts/plugins.html' },
             {
-              text: '组件外的 Store',
-              link: '/zh/core-concepts/outside-component-usage.html',
+              text: '入门',
+              link: '/zh/guide/',
+            },
+            {
+              text: '动态路由匹配',
+              link: '/zh/guide/essentials/dynamic-matching.html',
+            },
+            {
+              text: '路由的匹配语法',
+              link: '/zh/guide/essentials/route-matching-syntax.html',
+            },
+            {
+              text: '嵌套路由',
+              link: '/zh/guide/essentials/nested-routes.html',
+            },
+            {
+              text: '编程式导航',
+              link: '/zh/guide/essentials/navigation.html',
+            },
+            {
+              text: '命名路由',
+              link: '/zh/guide/essentials/named-routes.html',
             },
-          ],
-        },
-        {
-          text: '服务端渲染 (SSR)',
-          items: [
             {
-              text: 'Vue 与 Vite',
-              link: '/zh/ssr/',
+              text: '命名视图',
+              link: '/zh/guide/essentials/named-views.html',
             },
             {
-              text: 'Nuxt.js',
-              link: '/zh/ssr/nuxt.html',
+              text: '重定向和别名',
+              link: '/zh/guide/essentials/redirect-and-alias.html',
+            },
+            {
+              text: '路由组件传参',
+              link: '/zh/guide/essentials/passing-props.html',
+            },
+            {
+              text: '不同的历史记录模式',
+              link: '/zh/guide/essentials/history-mode.html',
             },
           ],
         },
         {
-          text: '手册',
-          collapsible: true,
-          collapsed: false,
+          text: '进阶',
+          collapsible: false,
           items: [
             {
-              text: '目录',
-              link: '/zh/cookbook/',
+              text: '导航守卫',
+              link: '/zh/guide/advanced/navigation-guards.html',
             },
             {
-              text: '从 Vuex ≤4 迁移',
-              link: '/zh/cookbook/migration-vuex.html',
+              text: '路由元信息',
+              link: '/zh/guide/advanced/meta.html',
             },
             {
-              text: '热更新',
-              link: '/zh/cookbook/hot-module-replacement.html',
+              text: '数据获取',
+              link: '/zh/guide/advanced/data-fetching.html',
             },
             {
-              text: '测试',
-              link: '/zh/cookbook/testing.html',
+              text: '组合式 API',
+              link: '/zh/guide/advanced/composition-api.html',
             },
             {
-              text: '不使用 setup() 的用法',
-              link: '/zh/cookbook/options-api.html',
+              text: '过渡动效',
+              link: '/zh/guide/advanced/transitions.html',
             },
             {
-              text: '组合式 Stores',
-              link: '/zh/cookbook/composing-stores.html',
+              text: '滚动行为',
+              link: '/zh/guide/advanced/scroll-behavior.html',
             },
             {
-              text: '从 v0/v1 迁移至 v2',
-              link: '/zh/cookbook/migration-v1-v2.html',
+              text: '路由懒加载',
+              link: '/zh/guide/advanced/lazy-loading.html',
             },
             {
-              text: '处理组合式函数',
-              link: '/zh/cookbook/composables.html',
+              text: '扩展 RouterLink',
+              link: '/zh/guide/advanced/extending-router-link.html',
+            },
+            {
+              text: '导航故障',
+              link: '/zh/guide/advanced/navigation-failures.html',
+            },
+            {
+              text: '动态路由',
+              link: '/zh/guide/advanced/dynamic-routing.html',
+            },
+          ],
+        },
+        {
+          items: [
+            {
+              text: '从 Vue2 迁移',
+              link: '/zh/guide/migration/index.html',
             },
           ],
         },
index b514f93426552cd197bb74ba43df6c598fd81066..2929cdc80a98b144a27621287d267a0d62ddabc4 100644 (file)
@@ -8,14 +8,14 @@
   border-radius: 2em;
   transition: all 0.15s ease;
   box-sizing: border-box;
-  border: 2px solid var(--c-yellow-dark);
+  border: 2px solid var(--vp-c-brand-dark);
 }
 
 .become-sponsor:hover {
-  background-color: var(--c-yellow);
+  background-color: var(--vp-c-brand);
   text-decoration: none;
-  border-color: var(--c-yellow);
-  color: var(--c-text-light-1);
+  border-color: var(--vp-c-brand);
+  color: var(--vp-button-brand-text);
 }
 
 .sponsors-top .become-sponsor {
diff --git a/packages/docs/zh/api/index.md b/packages/docs/zh/api/index.md
new file mode 100644 (file)
index 0000000..f21a811
--- /dev/null
@@ -0,0 +1,1118 @@
+---
+sidebar: auto
+---
+
+# API 参考
+
+## `<router-link>` Props
+
+### to
+
+- **类型**:[`RouteLocationRaw`](#routelocationraw)
+- **详细内容**:
+
+  表示目标路由的链接。当被点击后,内部会立刻把 `to` 的值传到 `router.push()`,所以这个值可以是一个 `string` 或者是[描述目标位置的对象](#routelocationraw)。
+
+```html
+<!-- 字符串 -->
+<router-link to="/home">Home</router-link>
+<!-- 渲染结果 -->
+<a href="/home">Home</a>
+
+<!-- 使用 v-bind 的 JS 表达式 -->
+<router-link :to="'/home'">Home</router-link>
+
+<!-- 同上 -->
+<router-link :to="{ path: '/home' }">Home</router-link>
+
+<!-- 命名的路由 -->
+<router-link :to="{ name: 'user', params: { userId: '123' }}">User</router-link>
+
+<!-- 带查询参数,下面的结果为 `/register?plan=private` -->
+<router-link :to="{ path: '/register', query: { plan: 'private' }}">
+  Register
+</router-link>
+```
+
+### replace
+
+- **类型**:`boolean`
+- **默认值**:`false`
+- **详细内容**:
+
+  设置 `replace` 属性的话,当点击时,会调用 `router.replace()`,而不是 `router.push()`,所以导航后不会留下历史记录。
+
+```html
+<router-link to="/abc" replace></router-link>
+```
+
+### active-class
+
+- **类型**:`string`
+- **默认值**:`"router-link-active"` (或者全局 [`linkActiveClass`](#linkactiveclass))
+- **详细内容**:
+
+  链接激活时,应用于渲染的 `<a>` 的 class。
+
+### aria-current-value
+
+- **类型**:`'page' | 'step' | 'location' | 'date' | 'time' | 'true' | 'false'` (`string`)
+- **默认值**:`"page"`
+- **详细内容**:
+
+  当链接激活时,传递给属性 `aria-current` 的值。
+
+### custom
+
+- **类型**:`boolean`
+- **默认值**:`false`
+- **详细内容**:
+
+  `<router-link>` 是否应该将其内容包裹在 `<a>` 元素中。在使用 [`v-slot`](#router-link-s-v-slot) 创建自定义 RouterLink 时很有用。默认情况下,`<router-link>` 会将其内容包裹在 `<a>` 元素中,即使使用 `v-slot` 也是如此。传递 `custom` prop,可以去除这种行为。
+
+- **例如**:
+
+  ```html
+  <router-link to="/home" custom v-slot="{ navigate, href, route }">
+    <a :href="href" @click="navigate">{{ route.fullPath }}</a>
+  </router-link>
+  ```
+
+  渲染成 `<a href="/home">/home</a>`。
+
+  ```html
+  <router-link to="/home" v-slot="{ route }">
+    <span>{{ route.fullPath }}</span>
+  </router-link>
+  ```
+
+  渲染成 `<a href="/home"><span>/home</span></a>`。
+
+### exact-active-class
+
+- **类型**:`string`
+- **默认值**:`"router-link-exact-active"` (或者全局 [`linkExactActiveClass`](#linkexactactiveclass))
+- **详细内容**:
+
+  链接精准激活时,应用于渲染的 `<a>` 的 class。
+
+## `<router-link>` 的 `v-slot`
+
+`<router-link>` 通过一个[作用域插槽](https://v3.vuejs.org/guide/component-slots.html#scoped-slots)暴露底层的定制能力。这是一个更高阶的 API,主要面向库作者,但也可以为开发者提供便利,大多数情况下用在一个类似 _NavLink_ 这样的组件里。
+
+:::tip 注意
+记得把 `custom` 配置传递给 `<router-link>`,以防止它将内容包裹在 `<a>` 元素内。
+:::
+
+```html
+<router-link
+  to="/about"
+  custom
+  v-slot="{ href, route, navigate, isActive, isExactActive }"
+>
+  <NavLink :active="isActive" :href="href" @click="navigate">
+    {{ route.fullPath }}
+  </NavLink>
+</router-link>
+```
+
+- `href`:解析后的 URL。将会作为一个 `<a>` 元素的 `href` 属性。如果什么都没提供,则它会包含 `base`。
+- `route`:解析后的规范化的地址。
+- `navigate`:触发导航的函数。 **会在必要时自动阻止事件**,和 `router-link` 一样。例如:`ctrl` 或者 `cmd` + 点击仍然会被 `navigate` 忽略。
+- `isActive`:如果需要应用 [active class](#active-class),则为 `true`。允许应用一个任意的 class。
+- `isExactActive`:如果需要应用 [exact active class](#exact-active-class),则为 `true`。允许应用一个任意的 class。
+
+### 示例:将激活的 class 应用在外层元素
+
+有时我们可能想把激活的 class 应用到一个外部元素而不是 `<a>` 标签本身,这时你可以在一个 `router-link` 中包裹该元素并使用 `v-slot` 属性来创建链接:
+
+```html
+<router-link
+  to="/foo"
+  custom
+  v-slot="{ href, route, navigate, isActive, isExactActive }"
+>
+  <li
+    :class="[isActive && 'router-link-active', isExactActive && 'router-link-exact-active']"
+  >
+    <a :href="href" @click="navigate">{{ route.fullPath }}</a>
+  </li>
+</router-link>
+```
+
+:::tip 提示
+如果你在 `a` 元素上添加一个 `target="_blank"`,你必须省略 `@click="navigate"` 的处理。
+:::
+
+## `<router-view>` Props
+
+### name
+
+- **类型**:`string`
+- **默认值**:`"default"`
+- **详细内容**:
+
+  如果 `<router-view>` 设置了 `name`,则会渲染对应的路由配置中 `components` 下的相应组件。
+
+- **更多的内容请看**:[命名视图](../guide/essentials/named-views.md)
+
+### route
+
+- **类型**:[`RouteLocationNormalized`](#routelocationnormalized)
+- **详细内容**:
+
+  一个路由地址的所有组件都已被解析(如果所有组件都被懒加载),因此可以显示。
+
+## `<router-view>` 的 `v-slot`
+
+`<router-view>` 暴露了一个 `v-slot` API,主要使用 `<transition>` 和 `<keep-alive>` 组件来包裹你的路由组件。
+
+```html
+<router-view v-slot="{ Component, route }">
+  <transition :name="route.meta.transition || 'fade'" mode="out-in">
+    <keep-alive>
+      <suspense>
+        <template #default>
+          <component
+            :is="Component"
+            :key="route.meta.usePathKey ? route.path : undefined"
+          />
+        </template>
+        <template #fallback> Loading... </template>
+      </suspense>
+    </keep-alive>
+  </transition>
+</router-view>
+```
+
+- `Component`: VNodes, 传递给 `<component>`的`is` prop。
+- `route`: 解析出的标准化[路由地址](#routelocationnormalized)。
+
+## createRouter
+
+创建一个可以被 Vue 应用程序使用的路由实例。查看 [`RouterOptions`](#routeroptions) 中的所有可以传递的属性列表。
+
+**函数签名:**
+
+```typescript
+export declare function createRouter(options: RouterOptions): Router
+```
+
+### 参数
+
+| 参数    | 类型                            | 描述                      |
+| ------- | ------------------------------- | ------------------------- |
+| options | [RouterOptions](#routeroptions) | Options 用来初始化 router |
+
+## createWebHistory
+
+创建一个 HTML5 历史,即单页面应用程序中最常见的历史记录。应用程序必须通过 http 协议被提供服务。
+
+**函数签名:**
+
+```typescript
+export declare function createWebHistory(base?: string): RouterHistory
+```
+
+### 参数
+
+| 参数 | 类型     | 描述                                                                                             |
+| ---- | -------- | ------------------------------------------------------------------------------------------------ |
+| base | `string` | 提供的可选 base。当应用程序被托管在诸如 `https://example.com/folder/` 之类的文件夹中时非常有用。 |
+
+### 示例
+
+```js
+createWebHistory() // 没有 base,应用托管在域名 `https://example.com` 的根目录下。
+createWebHistory('/folder/') // 给出的网址为 `https://example.com/folder/`
+```
+
+## createWebHashHistory
+
+创建一个 hash 历史记录。对于没有主机的 web 应用程序 (例如 `file://`),或当配置服务器不能处理任意 URL 时这非常有用。**注意:如果 SEO 对你很重要,你应该使用 [`createWebHistory`](#createwebhistory)**。
+
+**函数签名:**
+
+```typescript
+export declare function createWebHashHistory(base?: string): RouterHistory
+```
+
+### 参数
+
+| 参数 | 类型     | 描述                                                                                                                                                                                                                                                                                        |
+| ---- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| base | `string` | 提供可选的 base。默认是 `location.pathname + location.search`。如果 `head` 中有一个 `<base>`,它的值将被忽略,而采用这个参数。**但请注意它会影响所有的 history.pushState() 调用**,这意味着如果你使用一个 `<base>` 标签,它的 `href` 值**必须与这个参数相匹配** (请忽略 `#` 后面的所有内容) |
+
+### 示例
+
+```js
+// at https://example.com/folder
+createWebHashHistory() // 给出的网址为 `https://example.com/folder#`
+createWebHashHistory('/folder/') // 给出的网址为 `https://example.com/folder/#`
+// 如果在 base 中提供了 `#`,则它不会被 `createWebHashHistory` 添加
+createWebHashHistory('/folder/#/app/') // 给出的网址为 `https://example.com/folder/#/app/`
+// 你应该避免这样做,因为它会更改原始 url 并打断正在复制的 url
+createWebHashHistory('/other-folder/') // 给出的网址为 `https://example.com/other-folder/#`
+
+// at file:///usr/etc/folder/index.html
+// 对于没有 `host` 的位置,base被忽略
+createWebHashHistory('/iAmIgnored') // 给出的网址为 `file:///usr/etc/folder/index.html#`
+```
+
+## createMemoryHistory
+
+创建一个基于内存的历史记录。这个历史记录的主要目的是处理 SSR。它在一个特殊的位置开始,这个位置无处不在。如果用户不在浏览器上下文中,它们可以通过调用 `router.push()` 或 `router.replace()` 将该位置替换为启动位置。
+
+**函数签名:**
+
+```typescript
+export declare function createMemoryHistory(base?: string): RouterHistory
+```
+
+### 参数
+
+| 参数 | 类型     | 描述                           |
+| ---- | -------- | ------------------------------ |
+| base | `string` | Base 适用于所有 URL,默认为'/' |
+
+### Returns
+
+一个可以传递给路由构造函数的历史对象。
+
+## NavigationFailureType
+
+包含所有可能导航失败类型的枚举,可以传递给 [isNavigationFailure](#isnavigationfailure) 来检查某些特定类型的失败。**不要使用任何数值**,总是使用诸如 `NavigationFailureType.aborted` 这样的变量。
+
+**函数签名:**
+
+```typescript
+export declare enum NavigationFailureType
+```
+
+### 成员
+
+| 成员       | 值  | 描述                                                                     |
+| ---------- | --- | ------------------------------------------------------------------------ |
+| aborted    | 4   | 终止导航是指由于导航守卫返回 `false` 或调用 `next(false)` 而失败的导航。 |
+| cancelled  | 8   | 取消导航是指由于最近的导航完成启动(不一定是完成)而失败的导航。         |
+| duplicated | 16  | 重复导航是指在启动时已经在同一位置失败的导航。                           |
+
+## START_LOCATION
+
+- **类型**:[`RouteLocationNormalized`](#routelocationnormalized)
+- **详细内容**:
+
+  路由所在的初始路由地址。可用于导航守卫中,以区分初始导航。
+
+  ```js
+  import { START_LOCATION } from 'vue-router'
+
+  router.beforeEach((to, from) => {
+    if (from === START_LOCATION) {
+      // 初始导航
+    }
+  })
+  ```
+
+## Composition API
+
+### onBeforeRouteLeave
+
+添加一个导航守卫,在当前位置的组件将要离开时触发。类似于 `beforeRouteLeave`,但它可以在任何组件中使用。当组件被卸载时,导航守卫将被移除。
+
+**函数签名:**
+
+```typescript
+export declare function onBeforeRouteLeave(leaveGuard: NavigationGuard): void
+```
+
+#### 参数
+
+| 参数       | 类型                                  | 描述             |
+| ---------- | ------------------------------------- | ---------------- |
+| leaveGuard | [`NavigationGuard`](#navigationguard) | 要添加的导航守卫 |
+
+### onBeforeRouteUpdate
+
+添加一个导航守卫,在当前位置即将更新时触发。类似于 `beforeRouteUpdate`,但它可以在任何组件中使用。当组件被卸载时,导航守卫将被移除。
+
+**函数签名:**
+
+```typescript
+export declare function onBeforeRouteUpdate(updateGuard: NavigationGuard): void
+```
+
+#### 参数
+
+| 参数        | 类型                                  | 描述             |
+| ----------- | ------------------------------------- | ---------------- |
+| updateGuard | [`NavigationGuard`](#navigationguard) | 要添加的导航守卫 |
+
+### useLink
+
+返回 [`v-slot` API](#router-link-s-v-slot) 暴露的所有内容。
+
+**函数签名:**
+
+```typescript
+export declare function useLink(props: RouterLinkOptions): {
+  route: ComputedRef<RouteLocationNormalized & { href: string }>,
+  href: ComputedRef<string>,
+  isActive: ComputedRef<boolean>,
+  isExactActive: ComputedRef<boolean>,
+  navigate: (event?: MouseEvent) => Promise(NavigationFailure | void),
+}
+```
+
+#### 参数
+
+| 参数  | 类型                | 描述                                                             |
+| ----- | ------------------- | ---------------------------------------------------------------- |
+| props | `RouterLinkOptions` | props 对象可以传递给`<router-link>`。接收 `Ref` 和 `ComputedRef` |
+
+### useRoute
+
+返回当前路由地址。相当于在模板中使用 `$route`。必须在 `setup()` 中调用。
+
+**函数签名:**
+
+```typescript
+export declare function useRoute(): RouteLocationNormalized
+```
+
+### useRouter
+
+返回 [router](#router-properties) 实例。相当于在模板中使用 `$router`。必须在 `setup()` 中调用。
+
+**函数签名:**
+
+```typescript
+export declare function useRouter(): Router
+```
+
+## TypeScript
+
+下面是 Vue Router 使用的一些接口和类型。文档引用它们是为了让你了解对象中现有的属性。
+
+## Router 属性
+
+### currentRoute
+
+- **类型**:[`Ref<RouteLocationNormalized>`](#routelocationnormalized)
+- **详细内容**:
+
+  当前路由地址。只读的。
+
+### options
+
+- **类型**:[`RouterOptions`](#routeroptions)
+- **详细内容**:
+
+  创建 Router 时传递的原始配置对象。只读的。
+
+## Router 方法
+
+### addRoute
+
+添加一条新的[路由记录](#routerecordraw)作为现有路由的子路由。如果路由有一个 `name`,并且已经有一个与之名字相同的路由,它会先删除之前的路由。
+
+**函数签名:**
+
+```typescript
+addRoute(parentName: string | symbol, route: RouteRecordRaw): () => void
+```
+
+_参数_
+
+| 参数       | 类型                                | 描述                                   |
+| ---------- | ----------------------------------- | -------------------------------------- |
+| parentName | `string \| symbol`                  | 父路由记录,`route` 应该被添加到的位置 |
+| route      | [`RouteRecordRaw`](#routerecordraw) | 要添加的路由记录                       |
+
+### addRoute
+
+添加一条新的[路由记录](#routerecordraw)到路由。如果路由有一个 `name`,并且已经有一个与之名字相同的路由,它会先删除之前的路由。
+
+**函数签名:**
+
+```typescript
+addRoute(route: RouteRecordRaw): () => void
+```
+
+_参数_
+
+| 参数  | 类型                                | 描述             |
+| ----- | ----------------------------------- | ---------------- |
+| route | [`RouteRecordRaw`](#routerecordraw) | 要添加的路由记录 |
+
+:::tip 提示
+请注意,添加路由并不会触发新的导航。也就是说,除非触发新的导航,否则不会显示所添加的路由。
+:::
+
+### afterEach
+
+添加一个导航钩子,在每次导航后执行。返回一个删除注册钩子的函数。
+
+**函数签名:**
+
+```typescript
+afterEach(guard: NavigationHookAfter): () => void
+```
+
+_参数_
+
+| 参数  | 类型                  | 描述             |
+| ----- | --------------------- | ---------------- |
+| guard | `NavigationHookAfter` | 要添加的导航钩子 |
+
+#### 示例
+
+```js
+router.afterEach((to, from, failure) => {
+  if (isNavigationFailure(failure)) {
+    console.log('failed navigation', failure)
+  }
+})
+```
+
+### back
+
+如果可能的话,通过调用 `history.back()` 回溯历史。相当于 `router.go(-1)`。
+
+**函数签名:**
+
+```typescript
+back(): void
+```
+
+### beforeEach
+
+添加一个导航守卫,在任何导航前执行。返回一个删除已注册守卫的函数。
+
+**函数签名:**
+
+```typescript
+beforeEach(guard: NavigationGuard): () => void
+```
+
+_参数_
+
+| 参数  | 类型                                  | 描述             |
+| ----- | ------------------------------------- | ---------------- |
+| guard | [`NavigationGuard`](#navigationguard) | 要添加的导航守卫 |
+
+### beforeResolve
+
+添加一个导航守卫,在导航即将解析之前执行。在这个状态下,所有的组件都已经被获取,并且其他导航守卫也已经成功。返回一个删除已注册守卫的函数。
+
+**函数签名:**
+
+```typescript
+beforeResolve(guard: NavigationGuard): () => void
+```
+
+_参数_
+
+| 参数  | 类型                                  | 描述             |
+| ----- | ------------------------------------- | ---------------- |
+| guard | [`NavigationGuard`](#navigationguard) | 要添加的导航守卫 |
+
+#### 示例
+
+```js
+router.beforeResolve(to => {
+  if (to.meta.requiresAuth && !isAuthenticated) return false
+})
+```
+
+### forward
+
+如果可能的话,通过调用 `history.forward()` 在历史中前进。相当于 `router.go(1)`。
+
+**函数签名:**
+
+```typescript
+forward(): void
+```
+
+### getRoutes
+
+获取所有 [路由记录](#routerecordnormalized)的完整列表。
+
+**函数签名:**
+
+```typescript
+getRoutes(): RouteRecord[]
+```
+
+### go
+
+允许你在历史中前进或后退。
+
+**函数签名:**
+
+```typescript
+go(delta: number): void
+```
+
+_参数_
+
+| 参数  | 类型     | 描述                                 |
+| ----- | -------- | ------------------------------------ |
+| delta | `number` | 相对于当前页面,你要移动到的历史位置 |
+
+### hasRoute
+
+确认是否存在指定名称的路由。
+
+**函数签名:**
+
+```typescript
+hasRoute(name: string | symbol): boolean
+```
+
+_参数_
+
+| 参数 | 类型               | 描述             |
+| ---- | ------------------ | ---------------- |
+| name | `string \| symbol` | 要确认的路由名称 |
+
+### isReady
+
+当路由器完成初始化导航时,返回一个 Promise,这意味着它已经解析了所有与初始路由相关的异步输入钩子和异步组件。如果初始导航已经发生了,那么 promise 就会立即解析。这在服务器端渲染中很有用,可以确保服务器和客户端的输出一致。需要注意的是,在服务器端,你需要手动推送初始位置,而在客户端,路由器会自动从 URL 中获取初始位置。
+
+**函数签名:**
+
+```typescript
+isReady(): Promise<void>
+```
+
+### onError
+
+添加一个错误处理程序,在导航期间每次发生未捕获的错误时都会调用该处理程序。这包括同步和异步抛出的错误、在任何导航守卫中返回或传递给 `next` 的错误,以及在试图解析渲染路由所需的异步组件时发生的错误。
+
+**函数签名:**
+
+```typescript
+onError(handler: (error: any, to: RouteLocationNormalized, from: RouteLocationNormalized) => any): () => void
+```
+
+_参数_
+
+| 参数    | 类型                                                                              | 描述                      |
+| ------- | --------------------------------------------------------------------------------- | ------------------------- |
+| handler | `(error: any, to: RouteLocationNormalized, from: RouteLocationNormalized) => any` | 注册的错误处理程序 |
+
+### push
+
+通过在历史堆栈中推送一个 entry,以编程方式导航到一个新的 URL。
+
+**函数签名:**
+
+```typescript
+push(to: RouteLocationRaw): Promise<NavigationFailure | void | undefined>
+```
+
+_参数_
+
+| 参数 | 类型                                    | 描述               |
+| ---- | --------------------------------------- | ------------------ |
+| to   | [`RouteLocationRaw`](#routelocationraw) | 要导航到的路由地址 |
+
+### removeRoute
+
+通过名称删除现有路由。
+
+**函数签名:**
+
+```typescript
+removeRoute(name: string | symbol): void
+```
+
+_参数_
+
+| 参数 | 类型               | 描述             |
+| ---- | ------------------ | ---------------- |
+| name | `string \| symbol` | 要删除的路由名称 |
+
+### replace
+
+通过替换历史堆栈中的当前 entry,以编程方式导航到一个新的 URL。
+
+**函数签名:**
+
+```typescript
+replace(to: RouteLocationRaw): Promise<NavigationFailure | void | undefined>
+```
+
+_参数_
+
+| 参数 | 类型                                    | 描述               |
+| ---- | --------------------------------------- | ------------------ |
+| to   | [`RouteLocationRaw`](#routelocationraw) | 要导航到的路由地址 |
+
+### resolve
+
+返回[路由地址](#routelocationraw)的[标准化版本](#routelocation)。还包括一个包含任何现有 `base` 的 `href` 属性。
+
+**函数签名:**
+
+```typescript
+resolve(to: RouteLocationRaw): RouteLocation & {
+  href: string
+}
+```
+
+_参数_
+
+| 参数 | 类型                                    | 描述                 |
+| ---- | --------------------------------------- | -------------------- |
+| to   | [`RouteLocationRaw`](#routelocationraw) | 要解析的原始路由地址 |
+
+## RouterOptions
+
+### history
+
+用于路由实现历史记录。大多数 web 应用程序都应该使用 `createWebHistory`,但它要求正确配置服务器。你还可以使用 `createWebHashHistory` 的基于 _hash_ 的历史记录,它不需要在服务器上进行任何配置,但是搜索引擎根本不会处理它,在 SEO 上表现很差。
+
+**函数签名:**
+
+```typescript
+history: RouterHistory
+```
+
+#### 示例
+
+```js
+createRouter({
+  history: createWebHistory(),
+  // 其他配置...
+})
+```
+
+### linkActiveClass
+
+用于激活的 [RouterLink](#router-link-props) 的默认类。如果什么都没提供,则会使用 `router-link-active`。
+
+**函数签名:**
+
+```typescript
+linkActiveClass?: string
+```
+
+### linkExactActiveClass
+
+用于精准激活的 [RouterLink](#router-link-props) 的默认类。如果什么都没提供,则会使用 `router-link-exact-active`。
+
+**函数签名:**
+
+```typescript
+linkExactActiveClass?: string
+```
+
+### parseQuery
+
+用于解析查询的自定义实现。必须解码查询键和值。参见对应的 [stringifyQuery](#stringifyquery)。
+
+**函数签名:**
+
+```typescript
+parseQuery?: (searchQuery: string) => Record<string, (string | null)[] | string | null>
+```
+
+#### 示例
+
+比方说,你想使用 [qs](https://github.com/ljharb/qs) 包来解析查询,你可以同时提供 `parseQuery` 和 `stringifyQuery`:
+
+```js
+import qs from 'qs'
+
+createRouter({
+  // 其他配置...
+  parseQuery: qs.parse,
+  stringifyQuery: qs.stringify,
+})
+```
+
+### routes
+
+应该添加到路由的初始路由列表。
+
+**函数签名:**
+
+```typescript
+routes: RouteRecordRaw[]
+```
+
+### scrollBehavior
+
+在页面之间导航时控制滚动的函数。可以返回一个 Promise 来延迟滚动。有关更多详细信息,请参见[滚动行为](../guide/advanced/scroll-behavior.md)。
+
+**函数签名:**
+
+```typescript
+scrollBehavior?: RouterScrollBehavior
+```
+
+#### 示例
+
+```js
+function scrollBehavior(to, from, savedPosition) {
+  // `to` 和 `from` 都是路由地址
+  // `savedPosition` 可以为空,如果没有的话。
+}
+```
+
+### stringifyQuery
+
+对查询对象进行字符串化的自定义实现。不应该在前面加上 `?`。应该正确编码查询键和值。 [parseQuery](#parsequery) 对应于处理查询解析。
+
+**函数签名:**
+
+```typescript
+stringifyQuery?: (
+  query: Record<
+    string | number,
+    string | number | null | undefined | (string | number | null | undefined)[]
+  >
+) => string
+```
+
+## RouteRecordRaw
+
+当用户通过 [`routes` option](#routeroptions) 或者 [`router.addRoute()`](#addroute) 来添加路由时,可以得到路由记录。 有三种不同的路由记录:
+
+- 单一视图记录:有一个 `component` 配置
+- 多视图记录 ([命名视图](../guide/essentials/named-views.md)) :有一个 `components` 配置
+- 重定向记录:没有 `component` 或 `components` 配置,因为重定向记录永远不会到达。
+
+### path
+
+- **类型**:`string`
+- **详细内容**:
+
+  记录的路径。应该以 `/` 开头,除非该记录是另一条记录的子记录。可以定义参数:`/users/:id` 匹配 `/users/1` 以及 `/users/posva`。
+
+- **更多的内容请看**:[动态路由匹配](../guide/essentials/dynamic-matching.md)
+
+### redirect
+
+- **类型**:`RouteLocationRaw | (to: RouteLocationNormalized) => RouteLocationRaw` (可选)
+- **详细内容**:
+
+  如果路由是直接匹配的,那么重定向到哪里呢。重定向发生在所有导航守卫之前,并以新的目标位置触发一个新的导航。也可以是一个接收目标路由地址并返回我们应该重定向到的位置的函数。
+
+### children
+
+- **类型**:[`RouteRecordRaw`](#routerecordraw) 数组 (可选)
+- **详细内容**:
+
+  当前记录的嵌套路由。
+
+- **更多的内容请看**:[Nested Routes](../guide/essentials/nested-routes.md)
+
+### alias
+
+- **类型**:`string | string[]` (可选)
+- **详细内容**:
+
+  路由的别名。允许定义类似记录副本的额外路由。这使得路由可以简写为像这种 `/users/:id` 和 `/u/:id`。 **所有的 `alias` 和 `path` 值必须共享相同的参数**。
+
+### name
+
+- **类型**:`string | symbol` (可选)
+- **详细内容**:
+
+  路由记录独一无二的名称。
+
+### beforeEnter
+
+- **类型**:[`NavigationGuard | NavigationGuard[]`](#navigationguard) (可选)
+- **详细内容**:
+
+  在进入特定于此记录的守卫之前。注意如果记录有`重定向`属性,则 `beforeEnter` 无效。
+
+### props
+
+- **类型**:`boolean | Record<string, any> | (to: RouteLocationNormalized) => Record<string, any>` (可选)
+- **详细内容**:
+
+  允许将参数作为 props 传递给由 `router-view` 渲染的组件。当传递给一个**多视图记录**时,它应该是一个与`组件`具有相同键的对象,或者是一个应用于每个组件的`布尔值`。
+
+- **更多的内容请看**:[给路由组件传 props](../guide/essentials/passing-props.md)
+
+### sensitive
+
+- **类型**: `boolean` (可选) 
+- **详细内容**: 
+
+  使路由匹配区分大小写,默认为`false`。注意这也可以在路由级别上设置。
+
+### strict
+
+- **类型**: `boolean` (可选) 
+- **详细内容**: 
+
+  严格检查路径末尾是否有尾部斜线(`/`)。默认为 `false`,意味着默认情况下,路由 `/users` 同时匹配 `/users` 和 `/users/`。注意这也可以在路由级别上设置。
+
+### meta
+
+- **类型**:[`RouteMeta`](#routemeta) (可选)
+- **详细内容**:
+
+  在记录上附加自定义数据。
+
+- **更多的内容请看**:[Meta 字段](../guide/advanced/meta.md)
+
+:::tip 注意
+如果你想使用函数式组件, 请确保在组件上添加一个 `displayName`。
+
+例如:
+
+```js
+const HomeView = () => h('div', 'HomePage')
+// 使用TypeScript时, 组件需要为 FunctionalComponent 类型
+HomeView.displayName = 'HomeView'
+const routes = [{ path: '/', component: HomeView }]
+```
+
+:::
+
+## RouteRecordNormalized
+
+[路由记录](#routerecordraw)的标准化版本
+
+### aliasOf
+
+- **类型**:`RouteRecordNormalized | undefined`
+- **详细内容**:
+
+  定义此记录是否是另一个记录的别名。如果该记录是原始记录,则此属性为 `undefined`。
+
+### beforeEnter
+
+- **类型**:[`NavigationGuard`](#navigationguard)
+- **详细内容**:
+
+  当从其他地方进入此记录时,导航守卫会被应用。
+
+- **更多的内容请看**:[导航守卫](../guide/advanced/navigation-guards.md)
+
+### children
+
+- **类型**:标准化[路由记录](#routerecordnormalized)数组
+- **详细内容**:
+
+  路由被添加时的子路由记录。如果没有则为空数组。注意这个数组在 `addRoute()` 和 `removeRoute()` 被调用时不会更新。
+
+### components
+
+- **类型**:`Record<string, Component>`
+- **详细内容**:
+
+  命名视图的字典,如果没有,包含一个键为 `default` 的对象。
+
+### meta
+
+- **类型**:`RouteMeta`
+- **详细内容**:
+
+  附在记录上的任意数据。
+
+- **更多的内容请看**:[Meta 字段](../guide/advanced/meta.md)
+
+### name
+
+- **类型**:`string | symbol | undefined`
+- **详细内容**:
+
+  路由记录的名称。如果什么都没提供,则为 `undefined`。
+
+### path
+
+- **类型**:`string`
+- **详细内容**:
+
+  路由记录的标准化路径。包括所有父级的 `path`。
+
+### props
+
+- **类型**:`Record<string, boolean | Function | Record<string, any>>`
+- **详细内容**:
+
+  每个命名视图的 [`props` 配置](#props)字典。如果没有,它将只包含一个名为 `default` 的属性。
+
+### redirect
+
+- **类型**:[`RouteLocationRaw`](#routelocationraw)
+- **详细内容**:
+
+  如果路由是直接匹配的,那么重定向到哪里呢。重定向发生在所有导航守卫之前,并触发一个带有新目标位置的新导航。
+
+## RouteLocationRaw
+
+用户级的路由地址,可以传递给 `router.push()`,`redirect`,并在[导航守卫](../guide/advanced/navigation-guards.md)中返回。
+
+原始位置可以是一个 `字符串`,比如 `/users/posva#bio`,也可以是一个对象:
+
+```js
+// 这三种形式是等价的
+router.push('/users/posva#bio')
+router.push({ path: '/users/posva', hash: '#bio' })
+router.push({ name: 'users', params: { username: 'posva' }, hash: '#bio' })
+// 只改变 hash
+router.push({ hash: '#bio' })
+// 只改变 query
+router.push({ query: { page: '2' } })
+// 只改变 param
+router.push({ params: { username: 'jolyne' } })
+```
+
+注意 `path` 必须以编码方式提供(例如,`phantom blood` 变为 `phantom%20blood`)。而 `params`、`query` 和 `hash` 一定不要这样,因为它们会被路由编码。
+
+原始路由地址还支持一个额外的配置 `replace` 来调用导航守卫中的 `router.replace()`,而不是 `router.push()`。请注意,即使在调用 `router.push()`时,它也会在内部调用 `router.replace()` :
+
+```js
+router.push({ hash: '#bio', replace: true })
+// 相当于
+router.replace({ hash: '#bio' })
+```
+
+## RouteLocation
+
+可以包含[重定向记录](#routerecordraw)的解析的 [RouteLocationRaw](#routelocationraw)。除此之外,它还具有与 [RouteLocationNormalized](#routelocationnormalized) 相同的属性。
+
+## RouteLocationNormalized
+
+标准化的路由地址。没有任何[重定向记录](#routerecordraw)。在导航守卫中,`to` 和 `from` 总是属于这种类型。
+
+### fullPath
+
+- **类型**:`string`
+- **详细内容**:
+
+  URL 编码与路由地址有关。包括 `path`、 `query` 和 `hash`。
+
+### hash
+
+- **类型**:`string`
+- **详细内容**:
+
+  已解码 URL 的 `hash` 部分。总是以 `#`开头。如果 URL 中没有 `hash`,则为空字符串。
+
+### query
+
+- **类型**:`Record<string, string | string[]>`
+- **详细内容**:
+
+  从 URL 的 `search` 部分提取的已解码查询参数的字典。
+
+### matched
+
+- **类型**:[`RouteRecordNormalized[]`](#routerecordnormalized)
+- **详细内容**:
+
+  与给定路由地址匹配的[标准化的路由记录](#routerecord)数组。
+
+### meta
+
+- **类型**:`RouteMeta`
+- **详细内容**:
+
+  附加到从父级到子级合并(非递归)的所有匹配记录的任意数据。
+
+- **更多的内容请看**:[Meta 字段](../guide/advanced/meta.md)
+
+### name
+
+- **类型**:`string | symbol | undefined | null`
+- **详细内容**:
+
+  路由记录的名称。如果什么都没提供,则为 `undefined`。
+
+### params
+
+- **类型**:`Record<string, string | string[]>`
+- **详细内容**:
+
+  从 `path` 中提取的已解码参数字典。
+
+### path
+
+- **类型**:`string`
+- **详细内容**:
+
+  编码 URL 的 `pathname` 部分,与路由地址有关。
+
+### redirectedFrom
+
+- **类型**:[`RouteLocation`](#routelocation)
+- **详细内容**:
+
+  在找到 `redirect` 配置或带有路由地址的名为 `next()` 的导航守卫时,我们最初尝试访问的路由地址,最后到达当前位置。如果没有重定向,则为 `undefined`。
+
+## NavigationFailure
+
+### from
+
+- **类型**:[`RouteLocationNormalized`](#routelocationnormalized)
+- **详细内容**:
+
+  导航来的路由地址
+
+### to
+
+- **类型**:[`RouteLocationNormalized`](#routelocationnormalized)
+- **详细内容**:
+
+  导航去的路由地址
+
+### type
+
+- **类型**:[`NavigationFailureType`](#navigationfailuretype)
+- **详细内容**:
+
+  导航失败的类型
+
+- **更多的内容请看**:[Navigation Failures](../guide/advanced/navigation-failures.md)
+
+## NavigationGuard
+
+- **Arguments**:
+
+  - [`RouteLocationNormalized`](#routelocationnormalized) to - 我们要导航到的路由地址
+  - [`RouteLocationNormalized`](#routelocationnormalized) from - 我们从哪里来的路由地址
+  - `Function` next (可选) - 回调以验证导航
+
+- **详细内容**:
+
+  可以通过函数来控制路由导航。如果你返回一个值(或一个 Promise ),则可以省略 `next` 回调,并且我们鼓励这样做。可能的返回值 (和 `next`的参数) 有:
+
+  - `undefined | void | true`: 验证导航
+  - `false`: 取消导航
+  - [`RouteLocationRaw`](#routelocationraw): 重定向到一个不同的位置
+  - `(vm: ComponentPublicInstance) => any` **仅适用于 `beforeRouteEnter`**:导航完成后执行的回调。接收路由组件实例作为参数。
+
+- **更多的内容请看**:[导航守卫](../guide/advanced/navigation-guards.md)
+
+## Component Injections
+
+### Component Injected 属性
+
+这些属性通过调用 `app.use(router)` 注入到每个子组件中。
+
+- **this.\$router**
+
+  router 实例
+
+- **this.\$route**
+
+  当前激活的[路由地址](#routelocationnormalized)。这个属性是只读的,并且它的属性是不可改变的,但是它可以被观察。
+
+### Component Enabled Options
+
+- **beforeRouteEnter**
+- **beforeRouteUpdate**
+- **beforeRouteLeave**
+
+请看[组件内的守卫](../guide/advanced/navigation-guards.md#组件内的守卫)。
diff --git a/packages/docs/zh/guide/advanced/composition-api.md b/packages/docs/zh/guide/advanced/composition-api.md
new file mode 100644 (file)
index 0000000..bfe18c0
--- /dev/null
@@ -0,0 +1,119 @@
+# Vue Router 和 组合式 API
+
+<VueSchoolLink
+  href="https://vueschool.io/lessons/router-and-the-composition-api"
+  title="Learn how to use Vue Router with the composition API"
+/>
+
+引入 `setup` 和 Vue 的[组合式 API](https://v3.vuejs.org/guide/composition-api-introduction.html),开辟了新的可能性,但要想充分发挥 Vue Router 的潜力,我们需要使用一些新的函数来代替访问 `this` 和组件内导航守卫。
+
+## 在 `setup` 中访问路由和当前路由
+
+因为我们在 `setup` 里面没有访问 `this`,所以我们不能再直接访问 `this.$router` 或 `this.$route`。作为替代,我们使用 `useRouter` 函数:
+
+```js
+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,
+        },
+      })
+    }
+  },
+}
+```
+
+`route` 对象是一个响应式对象,所以它的任何属性都可以被监听,但你应该**避免监听整个 `route`** 对象。在大多数情况下,你应该直接监听你期望改变的参数。
+
+```js
+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)
+      }
+    )
+  },
+}
+```
+
+请注意,在模板中我们仍然可以访问 `$router` 和 `$route`,所以不需要在 `setup` 中返回 `router` 或 `route`。
+
+## 导航守卫
+
+虽然你仍然可以通过 `setup` 函数来使用组件内的导航守卫,但 Vue Router 将更新和离开守卫作为 组合式 API 函数公开:
+
+```js
+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)
+      }
+    })
+  },
+}
+```
+
+组合式 API 守卫也可以用在任何由 `<router-view>` 渲染的组件中,它们不必像组件内守卫那样直接用在路由组件上。
+
+## `useLink`
+
+Vue Router 将 RouterLink 的内部行为作为一个组合式 API 函数公开。它提供了与 [`v-slot` API](../../api/#router-link-s-v-slot) 相同的访问属性:
+
+```js
+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, isActive, isExactActive, navigate } = useLink(props)
+
+    const isExternalLink = computed(
+      () => typeof props.to === 'string' && props.to.startsWith('http')
+    )
+
+    return { isExternalLink, href, navigate, isActive }
+  },
+}
+```
diff --git a/packages/docs/zh/guide/advanced/data-fetching.md b/packages/docs/zh/guide/advanced/data-fetching.md
new file mode 100644 (file)
index 0000000..e060fcf
--- /dev/null
@@ -0,0 +1,105 @@
+# 数据获取
+
+有时候,进入某个路由后,需要从服务器获取数据。例如,在渲染用户信息时,你需要从服务器获取用户的数据。我们可以通过两种方式来实现:
+
+- **导航完成之后获取**:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示“加载中”之类的指示。
+
+- **导航完成之前获取**:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航。
+
+从技术角度讲,两种方式都不错 —— 就看你想要的用户体验是哪种。
+
+## 导航完成后获取数据
+
+当你使用这种方式时,我们会马上导航和渲染组件,然后在组件的 created 钩子中获取数据。这让我们有机会在数据获取期间展示一个 loading 状态,还可以在不同视图间展示不同的 loading 状态。
+
+假设我们有一个 `Post` 组件,需要基于 `$route.params.id` 获取文章数据:
+
+```html
+<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>
+```
+
+```js
+export default {
+  data() {
+    return {
+      loading: false,
+      post: null,
+      error: null,
+    }
+  },
+  created() {
+    // watch 路由的参数,以便再次获取数据
+    this.$watch(
+      () => this.$route.params,
+      () => {
+        this.fetchData()
+      },
+      // 组件创建完后获取数据,
+      // 此时 data 已经被 observed 了
+      { immediate: true }
+    )
+  },
+  methods: {
+    fetchData() {
+      this.error = this.post = null
+      this.loading = true
+      // replace `getPost` with your data fetching util / API wrapper
+      getPost(this.$route.params.id, (err, post) => {
+        this.loading = false
+        if (err) {
+          this.error = err.toString()
+        } else {
+          this.post = post
+        }
+      })
+    },
+  },
+}
+```
+
+## 在导航完成前获取数据
+
+通过这种方式,我们在导航转入新的路由前获取数据。我们可以在接下来的组件的 `beforeRouteEnter` 守卫中获取数据,当数据获取成功后只调用 `next` 方法:
+
+```js
+export default {
+  data() {
+    return {
+      post: null,
+      error: null,
+    }
+  },
+  beforeRouteEnter(to, from, next) {
+    getPost(to.params.id, (err, post) => {
+      next(vm => vm.setData(err, post))
+    })
+  },
+  // 路由改变前,组件就已经渲染完了
+  // 逻辑稍稍不同
+  async beforeRouteUpdate(to, from) {
+    this.post = null
+    try {
+      this.post = await getPost(to.params.id)
+    } catch (error) {
+      this.error = error.toString()
+    }
+  },
+}
+```
+
+在为后面的视图获取数据时,用户会停留在当前的界面,因此建议在数据获取期间,显示一些进度条或者别的指示。如果数据获取失败,同样有必要展示一些全局的错误提醒。
+
+<!-- ### Using Composition API -->
+
+<!-- TODO: -->
diff --git a/packages/docs/zh/guide/advanced/dynamic-routing.md b/packages/docs/zh/guide/advanced/dynamic-routing.md
new file mode 100644 (file)
index 0000000..70991f1
--- /dev/null
@@ -0,0 +1,108 @@
+# 动态路由
+
+<VueSchoolLink
+  href="https://vueschool.io/lessons/vue-router-4-dynamic-routing"
+  title="Learn how to add routes at runtime"
+/>
+
+对路由的添加通常是通过 [`routes` 选项](../../api/#routes)来完成的,但是在某些情况下,你可能想在应用程序已经运行的时候添加或删除路由。具有可扩展接口(如 [Vue CLI UI](https://cli.vuejs.org/dev-guide/ui-api.html) )这样的应用程序可以使用它来扩展应用程序。
+
+## 添加路由
+
+动态路由主要通过两个函数实现。`router.addRoute()` 和 `router.removeRoute()`。它们**只**注册一个新的路由,也就是说,如果新增加的路由与当前位置相匹配,就需要你用 `router.push()` 或 `router.replace()` 来**手动导航**,才能显示该新路由。我们来看一个例子:
+
+想象一下,只有一个路由的以下路由:
+
+```js
+const router = createRouter({
+  history: createWebHistory(),
+  routes: [{ path: '/:articleName', component: Article }],
+})
+```
+
+进入任何页面,`/about`,`/store`,或者 `/3-tricks-to-improve-your-routing-code` 最终都会呈现 `Article` 组件。如果我们在 `/about` 上添加一个新的路由:
+
+```js
+router.addRoute({ path: '/about', component: About })
+```
+
+页面仍然会显示 `Article` 组件,我们需要手动调用 `router.replace()` 来改变当前的位置,并覆盖我们原来的位置(而不是添加一个新的路由,最后在我们的历史中两次出现在同一个位置):
+
+```js
+router.addRoute({ path: '/about', component: About })
+// 我们也可以使用 this.$route 或 route = useRoute() (在 setup 中)
+router.replace(router.currentRoute.value.fullPath)
+```
+
+记住,如果你需要等待新的路由显示,可以使用 `await router.replace()`。
+
+## 在导航守卫中添加路由
+
+如果你决定在导航守卫内部添加或删除路由,你不应该调用 `router.replace()`,而是通过返回新的位置来触发重定向:
+
+```js
+router.beforeEach(to => {
+  if (!hasNecessaryRoute(to)) {
+    router.addRoute(generateRoute(to))
+    // 触发重定向
+    return to.fullPath
+  }
+})
+```
+
+上面的例子有两个假设:第一,新添加的路由记录将与 `to` 位置相匹配,实际上导致与我们试图访问的位置不同。第二,`hasNecessaryRoute()` 在添加新的路由后返回 `false`,以避免无限重定向。
+
+因为是在重定向中,所以我们是在替换将要跳转的导航,实际上行为就像之前的例子一样。而在实际场景中,添加路由的行为更有可能发生在导航守卫之外,例如,当一个视图组件挂载时,它会注册新的路由。
+
+## 删除路由
+
+有几个不同的方法来删除现有的路由:
+
+- 通过添加一个名称冲突的路由。如果添加与现有途径名称相同的途径,会先删除路由,再添加路由:
+  ```js
+  router.addRoute({ path: '/about', name: 'about', component: About })
+  // 这将会删除之前已经添加的路由,因为他们具有相同的名字且名字必须是唯一的
+  router.addRoute({ path: '/other', name: 'about', component: Other })
+  ```
+- 通过调用 `router.addRoute()` 返回的回调:
+  ```js
+  const removeRoute = router.addRoute(routeRecord)
+  removeRoute() // 删除路由如果存在的话
+  ```
+  当路由没有名称时,这很有用。
+- 通过使用 `router.removeRoute()` 按名称删除路由:
+  ```js
+  router.addRoute({ path: '/about', name: 'about', component: About })
+  // 删除路由
+  router.removeRoute('about')
+  ```
+  需要注意的是,如果你想使用这个功能,但又想避免名字的冲突,可以在路由中使用 `Symbol` 作为名字。
+
+当路由被删除时,**所有的别名和子路由也会被同时删除**
+
+## 添加嵌套路由
+
+要将嵌套路由添加到现有的路由中,可以将路由的 _name_ 作为第一个参数传递给 `router.addRoute()`,这将有效地添加路由,就像通过 `children` 添加的一样:
+
+```js
+router.addRoute({ name: 'admin', path: '/admin', component: Admin })
+router.addRoute('admin', { path: 'settings', component: AdminSettings })
+```
+
+这等效于:
+
+```js
+router.addRoute({
+  name: 'admin',
+  path: '/admin',
+  component: Admin,
+  children: [{ path: 'settings', component: AdminSettings }],
+})
+```
+
+## 查看现有路由
+
+Vue Router 提供了两个功能来查看现有的路由:
+
+- [`router.hasRoute()`](../../api/#hasroute):检查路由是否存在。
+- [`router.getRoutes()`](../../api/#getroutes):获取一个包含所有路由记录的数组。
diff --git a/packages/docs/zh/guide/advanced/extending-router-link.md b/packages/docs/zh/guide/advanced/extending-router-link.md
new file mode 100644 (file)
index 0000000..b78507b
--- /dev/null
@@ -0,0 +1,94 @@
+# 扩展 RouterLink
+
+<VueSchoolLink
+  href="https://vueschool.io/lessons/extending-router-link-for-external-urls"
+  title="Learn how to extend router-link"
+/>
+
+RouterLink 组件提供了足够的 `props` 来满足大多数基本应用程序的需求,但它并未尝试涵盖所有可能的用例,在某些高级情况下,你可能会发现自己使用了 `v-slot`。在大多数中型到大型应用程序中,值得创建一个(如果不是多个)自定义 RouterLink 组件,以在整个应用程序中重用它们。例如导航菜单中的链接,处理外部链接,添加 `inactive-class` 等。
+
+让我们扩展 RouterLink 来处理外部链接,并在 `AppLink.vue` 文件中添加一个自定义的 `inactive-class`:
+
+```vue
+<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>
+
+<script>
+import { RouterLink } from 'vue-router'
+
+export default {
+  name: 'AppLink',
+  inheritAttrs: false,
+
+  props: {
+    // 如果使用 TypeScript,请添加 @ts-ignore
+    ...RouterLink.props,
+    inactiveClass: String,
+  },
+
+  computed: {
+    isExternalLink() {
+      return typeof this.to === 'string' && this.to.startsWith('http')
+    },
+  },
+}
+</script>
+```
+
+如果你喜欢使用渲染函数或创建 `computed` 属性,你可以使用 [Composition API](./composition-api.md) 中的 `useLink` :
+
+```js
+import { RouterLink, useLink } from 'vue-router'
+
+export default {
+  name: 'AppLink',
+
+  props: {
+    // 如果使用 TypeScript,请添加 @ts-ignore
+    ...RouterLink.props,
+    inactiveClass: String,
+  },
+
+  setup(props) {
+    // `props` 包含 `to` 和任何其他可以传递给 <router-link> 的 prop
+    const { navigate, href, route, isActive, isExactActive } = useLink(props)
+
+    // profit!
+
+    return { isExternalLink }
+  },
+}
+```
+
+在实践中,你可能希望将你的 `AppLink` 组件用于应用程序的不同部分。例如,使用 [Tailwind CSS](https://tailwindcss.com),你可以用所有的类创建一个 `NavLink.vue` 组件:
+
+```vue
+<template>
+  <AppLink
+    v-bind="$attrs"
+    class="inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 focus:outline-none transition duration-150 ease-in-out hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 transition duration-150 ease-in-out"
+    active-class="border-indigo-500 text-gray-900 focus:border-indigo-700"
+    inactive-class="text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:text-gray-700 focus:border-gray-300"
+  >
+    <slot />
+  </AppLink>
+</template>
+```
diff --git a/packages/docs/zh/guide/advanced/lazy-loading.md b/packages/docs/zh/guide/advanced/lazy-loading.md
new file mode 100644 (file)
index 0000000..40c1790
--- /dev/null
@@ -0,0 +1,81 @@
+# 路由懒加载
+
+<VueSchoolLink
+  href="https://vueschool.io/lessons/lazy-loading-routes-vue-cli-only"
+  title="Learn about lazy loading routes"
+/>
+
+当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就会更加高效。
+
+Vue Router 支持开箱即用的[动态导入](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports),这意味着你可以用动态导入代替静态导入:
+
+```js
+// 将
+// import UserDetails from './views/UserDetails.vue'
+// 替换成
+const UserDetails = () => import('./views/UserDetails.vue')
+
+const router = createRouter({
+  // ...
+  routes: [{ path: '/users/:id', component: UserDetails }],
+})
+```
+
+`component` (和 `components`) 配置接收一个返回 Promise 组件的函数,Vue Router **只会在第一次进入页面时才会获取这个函数**,然后使用缓存数据。这意味着你也可以使用更复杂的函数,只要它们返回一个 Promise :
+
+```js
+const UserDetails = () =>
+  Promise.resolve({
+    /* 组件定义 */
+  })
+```
+
+一般来说,对所有的路由**都使用动态导入**是个好主意。
+
+::: tip 注意
+**不要**在路由中使用[异步组件](https://v3.vuejs.org/guide/component-dynamic-async.html#async-components)。异步组件仍然可以在路由组件中使用,但路由组件本身就是动态导入的。
+:::
+
+如果你使用的是 webpack 之类的打包器,它将自动从[代码分割](https://webpack.js.org/guides/code-splitting/)中受益。
+
+如果你使用的是 Babel,你将需要添加 [syntax-dynamic-import](https://babeljs.io/docs/plugins/syntax-dynamic-import/) 插件,才能使 Babel 正确地解析语法。
+
+## 把组件按组分块
+
+### 使用 webpack
+
+有时候我们想把某个路由下的所有组件都打包在同个异步块 (chunk) 中。只需要使用[命名 chunk](https://webpack.js.org/guides/code-splitting/#dynamic-imports),一个特殊的注释语法来提供 chunk name (需要 Webpack > 2.4):
+
+```js
+const UserDetails = () =>
+  import(/* webpackChunkName: "group-user" */ './UserDetails.vue')
+const UserDashboard = () =>
+  import(/* webpackChunkName: "group-user" */ './UserDashboard.vue')
+const UserProfileEdit = () =>
+  import(/* webpackChunkName: "group-user" */ './UserProfileEdit.vue')
+```
+
+webpack 会将任何一个异步模块与相同的块名称组合到相同的异步块中。
+
+### 使用 Vite
+
+在Vite中,你可以在[`rollupOptions`](https://vitejs.dev/config/#build-rollupoptions)下定义分块:
+
+```js
+// vite.config.js
+export default defineConfig({
+  build: {
+    rollupOptions: {
+      // https://rollupjs.org/guide/en/#outputmanualchunks
+      output: {
+        manualChunks: {
+          'group-user': [
+            './src/UserDetails',
+            './src/UserDashboard',
+            './src/UserProfileEdit',
+          ],
+        },
+    },
+  },
+})
+```
diff --git a/packages/docs/zh/guide/advanced/meta.md b/packages/docs/zh/guide/advanced/meta.md
new file mode 100644 (file)
index 0000000..4e1ed64
--- /dev/null
@@ -0,0 +1,75 @@
+# 路由元信息
+
+<VueSchoolLink
+  href="https://vueschool.io/lessons/route-meta-fields"
+  title="Learn how to use route meta fields"
+/>
+
+有时,你可能希望将任意信息附加到路由上,如过渡名称、谁可以访问路由等。这些事情可以通过接收属性对象的`meta`属性来实现,并且它可以在路由地址和导航守卫上都被访问到。定义路由的时候你可以这样配置 `meta` 字段:
+
+```js
+const routes = [
+  {
+    path: '/posts',
+    component: PostsLayout,
+    children: [
+      {
+        path: 'new',
+        component: PostsNew,
+        // 只有经过身份验证的用户才能创建帖子
+        meta: { requiresAuth: true }
+      },
+      {
+        path: ':id',
+        component: PostsDetail
+        // 任何人都可以阅读文章
+        meta: { requiresAuth: false }
+      }
+    ]
+  }
+]
+```
+
+那么如何访问这个 `meta` 字段呢?
+
+<!-- TODO: the explanation about route records should be explained before and things should be moved here -->
+
+首先,我们称呼 `routes` 配置中的每个路由对象为 **路由记录**。路由记录可以是嵌套的,因此,当一个路由匹配成功后,它可能匹配多个路由记录。
+
+例如,根据上面的路由配置,`/posts/new` 这个 URL 将会匹配父路由记录 (`path: '/posts'`) 以及子路由记录 (`path: 'new'`)。
+
+一个路由匹配到的所有路由记录会暴露为 `$route` 对象(还有在导航守卫中的路由对象)的`$route.matched` 数组。我们需要遍历这个数组来检查路由记录中的 `meta` 字段,但是 Vue Router 还为你提供了一个 `$route.meta` 方法,它是一个非递归合并**所有 `meta`** 字段的(从父字段到子字段)的方法。这意味着你可以简单地写
+
+```js
+router.beforeEach((to, from) => {
+  // 而不是去检查每条路由记录
+  // to.matched.some(record => record.meta.requiresAuth)
+  if (to.meta.requiresAuth && !auth.isLoggedIn()) {
+    // 此路由需要授权,请检查是否已登录
+    // 如果没有,则重定向到登录页面
+    return {
+      path: '/login',
+      // 保存我们所在的位置,以便以后再来
+      query: { redirect: to.fullPath },
+    }
+  }
+})
+```
+
+## TypeScript
+
+可以通过扩展 `RouteMeta` 接口来输入 meta 字段:
+
+```ts
+// typings.d.ts or router.ts
+import 'vue-router'
+
+declare module 'vue-router' {
+  interface RouteMeta {
+    // 是可选的
+    isAdmin?: boolean
+    // 每个路由都必须声明
+    requiresAuth: boolean
+  }
+}
+```
diff --git a/packages/docs/zh/guide/advanced/navigation-failures.md b/packages/docs/zh/guide/advanced/navigation-failures.md
new file mode 100644 (file)
index 0000000..1324386
--- /dev/null
@@ -0,0 +1,98 @@
+# 等待导航结果
+
+<VueSchoolLink
+  href="https://vueschool.io/lessons/vue-router-4-detecting-navigation-failures"
+  title="Learn how to detect navigation failures"
+/>
+
+当使用 `router-link` 组件时,Vue Router 会自动调用 `router.push` 来触发一次导航。虽然大多数链接的预期行为是将用户导航到一个新页面,但也有少数情况下用户将留在同一页面上:
+
+- 用户已经位于他们正在尝试导航到的页面
+- 一个[导航守卫](./navigation-guards.md)通过调用 `return false` 中断了这次导航
+- 当前的导航守卫还没有完成时,一个新的导航守卫会出现了
+- 一个[导航守卫](./navigation-guards.md)通过返回一个新的位置,重定向到其他地方 (例如,`return '/login'`)
+- 一个[导航守卫](./navigation-guards.md)抛出了一个 `Error`
+
+如果我们想在一个导航完成后做一些事情,我们需要一个在调用 `router.push` 后进行等待的方法。想象一下,我们有一个移动手机菜单,它允许我们进入不同的页面,而我们只想在导航到新页面后隐藏菜单,我们可能想这样做:
+
+```js
+router.push('/my-profile')
+this.isMenuOpen = false
+```
+
+但是这样做会马上关闭菜单,因为 **导航是异步的**,我们需要 `await` `router.push` 返回的 promise :
+
+```js
+await router.push('/my-profile')
+this.isMenuOpen = false
+```
+
+现在,一旦导航完成,菜单就会关闭,但如果导航被阻止,它也会关闭。我们需要一种方法来检测我们是否真的改变了页面。
+
+## 检测导航故障
+
+如果导航被阻止,导致用户停留在同一个页面上,由 `router.push` 返回的 `Promise` 的解析值将是 _Navigation Failure_。否则,它将是一个 _falsy_ 值(通常是 `undefined`)。这样我们就可以区分我们导航是否离开了当前位置:
+
+```js
+const navigationResult = await router.push('/my-profile')
+
+if (navigationResult) {
+  // 导航被阻止
+} else {
+  // 导航成功 (包括重新导航的情况)
+  this.isMenuOpen = false
+}
+```
+
+_Navigation Failure_ 是带有一些额外属性的 `Error` 实例,这些属性为我们提供了足够的信息,让我们知道哪些导航被阻止了以及为什么被阻止了。要检查导航结果的性质,请使用 `isNavigationFailure` 函数:
+
+```js
+import { NavigationFailureType, isNavigationFailure } from 'vue-router'
+
+// 试图离开未保存的编辑文本界面
+const failure = await router.push('/articles/2')
+
+if (isNavigationFailure(failure, NavigationFailureType.aborted)) {
+  // 给用户显示一个小通知
+  showToast('You have unsaved changes, discard and leave anyway?')
+}
+```
+
+::: tip
+如果你忽略第二个参数: `isNavigationFailure(failure)`,那么就只会检查这个 `failure` 是不是一个 _Navigation Failure_。
+:::
+
+## 鉴别导航故障
+
+正如我们在一开始所说的,有不同的情况会导致导航的中止,所有这些情况都会导致不同的 _Navigation Failure_。它们可以用 `isNavigationFailure` 和 `NavigationFailureType` 来区分。总共有三种不同的类型:
+
+- `aborted`:在导航守卫中返回 `false` 中断了本次导航。
+- `cancelled`: 在当前导航还没有完成之前又有了一个新的导航。比如,在等待导航守卫的过程中又调用了 `router.push`。
+- `duplicated`:导航被阻止,因为我们已经在目标位置了。
+
+## *导航故障*的属性
+
+所有的导航失败都会暴露 `to` 和 `from` 属性,以反映失败导航的当前位置和目标位置:
+
+```js
+// 正在尝试访问 admin 页面
+router.push('/admin').then(failure => {
+  if (isNavigationFailure(failure, NavigationFailureType.redirected)) {
+    failure.to.path // '/admin'
+    failure.from.path // '/'
+  }
+})
+```
+
+在所有情况下,`to` 和 `from` 都是规范化的路由地址。
+
+## 检测重定向
+
+当在导航守卫中返回一个新的位置时,我们会触发一个新的导航,覆盖正在进行的导航。与其他返回值不同的是,重定向不会阻止导航,**而是创建一个新的导航**。因此,通过读取路由地址中的 `redirectedFrom` 属性,对其进行不同的检查:
+
+```js
+await router.push('/my-profile')
+if (router.currentRoute.value.redirectedFrom) {
+  // redirectedFrom 是解析出的路由地址,就像导航守卫中的 to和 from
+}
+```
diff --git a/packages/docs/zh/guide/advanced/navigation-guards.md b/packages/docs/zh/guide/advanced/navigation-guards.md
new file mode 100644 (file)
index 0000000..8ebc326
--- /dev/null
@@ -0,0 +1,264 @@
+# 导航守卫
+
+<VueSchoolLink
+  href="https://vueschool.io/lessons/route-guards"
+  title="Learn how to add navigation guards"
+/>
+
+正如其名,vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。这里有很多方式植入路由导航中:全局的,单个路由独享的,或者组件级的。
+
+## 全局前置守卫
+
+你可以使用 `router.beforeEach` 注册一个全局前置守卫:
+
+```js
+const router = createRouter({ ... })
+
+router.beforeEach((to, from) => {
+  // ...
+  // 返回 false 以取消导航
+  return false
+})
+```
+
+当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于**等待中**。
+
+每个守卫方法接收两个参数:
+
+- **`to`**: 即将要进入的目标 [用一种标准化的方式](../../api/#routelocationnormalized)
+- **`from`**: 当前导航正要离开的路由 [用一种标准化的方式](../../api/#routelocationnormalized)
+
+可以返回的值如下:
+
+- `false`: 取消当前的导航。如果浏览器的 URL 改变了(可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 `from` 路由对应的地址。
+- 一个[路由地址](../../api/#routelocationraw): 通过一个路由地址跳转到一个不同的地址,就像你调用 [`router.push()`](../../api/#push) 一样,你可以设置诸如 `replace: true` 或 `name: 'home'` 之类的配置。当前的导航被中断,然后进行一个新的导航,就和 `from` 一样。
+
+ ```js
+  router.beforeEach(async (to, from) => {
+    if (
+      // 检查用户是否已登录
+      !isAuthenticated &&
+      // ❗️ 避免无限重定向
+      to.name !== 'Login'
+    ) {
+      // 将用户重定向到登录页面
+      return { name: 'Login' }
+    }
+  })
+```
+
+如果遇到了意料之外的情况,可能会抛出一个 `Error`。这会取消导航并且调用 [`router.onError()`](../../api/#onerror) 注册过的回调。
+
+如果什么都没有,`undefined` 或返回 `true`,**则导航是有效的**,并调用下一个导航守卫
+
+以上所有都同 **`async` 函数** 和 Promise 工作方式一样:
+
+```js
+router.beforeEach(async (to, from) => {
+  // canUserAccess() 返回 `true` 或 `false`
+  const canAccess = await canUserAccess(to)
+  if (!canAccess) return '/login'
+})
+```
+
+### 可选的第三个参数 `next`
+
+在之前的 Vue Router 版本中,也是可以使用 _第三个参数_ `next` 的。这是一个常见的错误来源,可以通过 [RFC](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0037-router-return-guards.md#motivation) 来消除错误。然而,它仍然是被支持的,这意味着你可以向任何导航守卫传递第三个参数。在这种情况下,**确保 `next`** 在任何给定的导航守卫中都被**严格调用一次**。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错。这里有一个在用户未能验证身份时重定向到`/login`的**错误用例**:
+
+```js
+// BAD
+router.beforeEach((to, from, next) => {
+  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
+  // 如果用户未能验证身份,则 `next` 会被调用两次
+  next()
+})
+```
+
+下面是正确的版本:
+
+```js
+// GOOD
+router.beforeEach((to, from, next) => {
+  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
+  else next()
+})
+```
+
+## 全局解析守卫
+
+你可以用 `router.beforeResolve` 注册一个全局守卫。这和 `router.beforeEach` 类似,因为它在 **每次导航**时都会触发,但是确保在导航被确认之前,**同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被正确调用**。这里有一个例子,确保用户可以访问[自定义 meta](./meta.md) 属性 `requiresCamera` 的路由:
+
+```js
+router.beforeResolve(async to => {
+  if (to.meta.requiresCamera) {
+    try {
+      await askForCameraPermission()
+    } catch (error) {
+      if (error instanceof NotAllowedError) {
+        // ... 处理错误,然后取消导航
+        return false
+      } else {
+        // 意料之外的错误,取消导航并把错误传给全局处理器
+        throw error
+      }
+    }
+  }
+})
+```
+
+`router.beforeResolve` 是获取数据或执行任何其他操作(如果用户无法进入页面时你希望避免执行的操作)的理想位置。
+
+<!-- TODO: how to combine with [`meta` fields](./meta.md) to create a [generic fetching mechanism](#TODO). -->
+
+## 全局后置钩子
+
+你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 `next` 函数也不会改变导航本身:
+
+```js
+router.afterEach((to, from) => {
+  sendToAnalytics(to.fullPath)
+})
+```
+
+<!-- TODO: maybe add links to examples -->
+
+它们对于分析、更改页面标题、声明页面等辅助功能以及许多其他事情都很有用。
+
+它们也反映了 [navigation failures](./navigation-failures.md) 作为第三个参数:
+
+```js
+router.afterEach((to, from, failure) => {
+  if (!failure) sendToAnalytics(to.fullPath)
+})
+```
+
+了解更多关于 navigation failures 的信息在[它的指南](./navigation-failures.md)中。
+
+## 路由独享的守卫
+
+你可以直接在路由配置上定义 `beforeEnter` 守卫:
+
+```js
+const routes = [
+  {
+    path: '/users/:id',
+    component: UserDetails,
+    beforeEnter: (to, from) => {
+      // reject the navigation
+      return false
+    },
+  },
+]
+```
+
+`beforeEnter` 守卫 **只在进入路由时触发**,不会在 `params`、`query` 或 `hash` 改变时触发。例如,从 `/users/2` 进入到 `/users/3` 或者从 `/users/2#info` 进入到 `/users/2#projects`。它们只有在 **从一个不同的** 路由导航时,才会被触发。
+
+你也可以将一个函数数组传递给 `beforeEnter`,这在为不同的路由重用守卫时很有用:
+
+```js
+function removeQueryParams(to) {
+  if (Object.keys(to.query).length)
+    return { path: to.path, query: {}, hash: to.hash }
+}
+
+function removeHash(to) {
+  if (to.hash) return { path: to.path, query: to.query, hash: '' }
+}
+
+const routes = [
+  {
+    path: '/users/:id',
+    component: UserDetails,
+    beforeEnter: [removeQueryParams, removeHash],
+  },
+  {
+    path: '/about',
+    component: UserDetails,
+    beforeEnter: [removeQueryParams],
+  },
+]
+```
+
+请注意,你也可以通过使用[路径 meta 字段](./meta.md)和[全局导航守卫](#global-before-guards)来实现类似的行为。
+
+## 组件内的守卫
+
+最后,你可以在路由组件内直接定义路由导航守卫(传递给路由配置的)
+
+### 可用的配置 API
+
+你可以为路由组件添加以下配置:
+
+- `beforeRouteEnter`
+- `beforeRouteUpdate`
+- `beforeRouteLeave`
+
+```js
+const UserDetails = {
+  template: `...`,
+  beforeRouteEnter(to, from) {
+    // 在渲染该组件的对应路由被验证前调用
+    // 不能获取组件实例 `this` !
+    // 因为当守卫执行时,组件实例还没被创建!
+  },
+  beforeRouteUpdate(to, from) {
+    // 在当前路由改变,但是该组件被复用时调用
+    // 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候,
+    // 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
+    // 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
+  },
+  beforeRouteLeave(to, from) {
+    // 在导航离开渲染该组件的对应路由时调用
+    // 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
+  },
+}
+```
+
+`beforeRouteEnter` 守卫 **不能** 访问 `this`,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。
+
+不过,你可以通过传一个回调给 `next` 来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数:
+
+```js
+beforeRouteEnter (to, from, next) {
+  next(vm => {
+    // 通过 `vm` 访问组件实例
+  })
+}
+```
+
+注意 `beforeRouteEnter` 是支持给 `next` 传递回调的唯一守卫。对于 `beforeRouteUpdate` 和 `beforeRouteLeave` 来说,`this` 已经可用了,所以*不支持* 传递回调,因为没有必要了:
+
+```js
+beforeRouteUpdate (to, from) {
+  // just use `this`
+  this.name = to.params.name
+}
+```
+
+这个 **离开守卫** 通常用来预防用户在还未保存修改前突然离开。该导航可以通过返回 `false` 来取消。
+
+```js
+beforeRouteLeave (to, from) {
+  const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
+  if (!answer) return false
+}
+```
+
+### 使用组合 API
+
+如果你正在使用[组合 API 和 `setup` 函数](https://v3.vuejs.org/guide/composition-api-setup.html#setup)来编写组件,你可以通过 `onBeforeRouteUpdate` 和 `onBeforeRouteLeave` 分别添加 update 和 leave 守卫。 请参考[组合 API 部分](./composition-api.md#导航守卫)以获得更多细节。
+
+## 完整的导航解析流程
+
+1. 导航被触发。
+2. 在失活的组件里调用 `beforeRouteLeave` 守卫。
+3. 调用全局的 `beforeEach` 守卫。
+4. 在重用的组件里调用 `beforeRouteUpdate` 守卫(2.2+)。
+5. 在路由配置里调用 `beforeEnter`。
+6. 解析异步路由组件。
+7. 在被激活的组件里调用 `beforeRouteEnter`。
+8. 调用全局的 `beforeResolve` 守卫(2.5+)。
+9. 导航被确认。
+10. 调用全局的 `afterEach` 钩子。
+11. 触发 DOM 更新。
+12. 调用 `beforeRouteEnter` 守卫中传给 `next` 的回调函数,创建好的组件实例会作为回调函数的参数传入。
diff --git a/packages/docs/zh/guide/advanced/scroll-behavior.md b/packages/docs/zh/guide/advanced/scroll-behavior.md
new file mode 100644 (file)
index 0000000..c34d043
--- /dev/null
@@ -0,0 +1,113 @@
+# 滚动行为
+<VueSchoolLink
+  href="https://vueschool.io/lessons/scroll-behavior"
+  title="Learn how to customize scroll behavior"
+/>
+
+使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。 vue-router 能做到,而且更好,它让你可以自定义路由切换时页面如何滚动。
+
+**注意: 这个功能只在支持 history.pushState 的浏览器中可用。**
+
+当创建一个 Router 实例,你可以提供一个 `scrollBehavior` 方法:
+
+```js
+const router = createRouter({
+  history: createWebHashHistory(),
+  routes: [...],
+  scrollBehavior (to, from, savedPosition) {
+    // return 期望滚动到哪个的位置
+  }
+})
+```
+
+`scrollBehavior` 函数接收 `to`和` from` 路由对象,如 [Navigation Guards](./navigation-guards.md)。第三个参数 `savedPosition`,只有当这是一个 `popstate` 导航时才可用(由浏览器的后退/前进按钮触发)。
+
+该函数可以返回一个 [`ScrollToOptions`](https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions) 位置对象:
+
+```js
+const router = createRouter({
+  scrollBehavior(to, from, savedPosition) {
+    // 始终滚动到顶部
+    return { top: 0 }
+  },
+})
+```
+
+你也可以通过 `el` 传递一个 CSS 选择器或一个 DOM 元素。在这种情况下,`top` 和 `left` 将被视为该元素的相对偏移量。
+
+```js
+const router = createRouter({
+  scrollBehavior(to, from, savedPosition) {
+    // 始终在元素 #main 上方滚动 10px
+    return {
+      // 也可以这么写
+      // el: document.getElementById('main'),
+      el: '#main',
+      top: -10,
+    }
+  },
+})
+```
+
+如果返回一个 falsy 的值,或者是一个空对象,那么不会发生滚动。
+
+返回 `savedPosition`,在按下 后退/前进 按钮时,就会像浏览器的原生表现那样:
+
+```js
+const router = createRouter({
+  scrollBehavior(to, from, savedPosition) {
+    if (savedPosition) {
+      return savedPosition
+    } else {
+      return { top: 0 }
+    }
+  },
+})
+```
+
+如果你要模拟 “滚动到锚点” 的行为:
+
+```js
+const router = createRouter({
+  scrollBehavior(to, from, savedPosition) {
+    if (to.hash) {
+      return {
+        el: to.hash,
+      }
+    }
+  },
+})
+```
+
+如果你的浏览器支持[滚动行为](https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions/behavior),你可以让它变得更流畅:
+
+```js
+const router = createRouter({
+  scrollBehavior(to, from, savedPosition) {
+    if (to.hash) {
+      return {
+        el: to.hash,
+        behavior: 'smooth',
+      }
+    }
+  }
+})
+```
+
+## 延迟滚动
+
+有时候,我们需要在页面中滚动之前稍作等待。例如,当处理过渡时,我们希望等待过渡结束后再滚动。要做到这一点,你可以返回一个 Promise,它可以返回所需的位置描述符。下面是一个例子,我们在滚动前等待 500ms:
+
+```js
+const router = createRouter({
+  scrollBehavior(to, from, savedPosition) {
+    return new Promise((resolve, reject) => {
+      setTimeout(() => {
+        resolve({ left: 0, top: 0 })
+      }, 500)
+    })
+  },
+})
+```
+
+我们可以将其与页面级过渡组件的事件挂钩,以使滚动行为与你的页面过渡很好地结合起来,但由于使用场景可能存在的差异和复杂性,我们只是提供了这个基础来实现特定的用户场景。
diff --git a/packages/docs/zh/guide/advanced/transitions.md b/packages/docs/zh/guide/advanced/transitions.md
new file mode 100644 (file)
index 0000000..169606d
--- /dev/null
@@ -0,0 +1,84 @@
+# 过渡动效
+
+<VueSchoolLink
+  href="https://vueschool.io/lessons/route-transitions"
+  title="Learn about route transitions"
+/>
+
+想要在你的路径组件上使用转场,并对导航进行动画处理,你需要使用 [v-slot API](../../api/#router-view-s-v-slot):
+
+```html
+<router-view v-slot="{ Component }">
+  <transition name="fade">
+    <component :is="Component" />
+  </transition>
+</router-view>
+```
+
+[Transition 的 API](https://v3.vuejs.org/guide/transitions-enterleave.html) 在这里同样适用。
+
+## 单个路由的过渡
+
+上面的用法会对所有的路由使用相同的过渡。如果你想让每个路由的组件有不同的过渡,你可以将[元信息](./meta.md)和动态的 `name` 结合在一起,放在`<transition>` 上:
+
+```js
+const routes = [
+  {
+    path: '/custom-transition',
+    component: PanelLeft,
+    meta: { transition: 'slide-left' },
+  },
+  {
+    path: '/other-transition',
+    component: PanelRight,
+    meta: { transition: 'slide-right' },
+  },
+]
+```
+
+```html
+<router-view v-slot="{ Component, route }">
+  <!-- 使用任何自定义过渡和回退到 `fade` -->
+  <transition :name="route.meta.transition || 'fade'">
+    <component :is="Component" />
+  </transition>
+</router-view>
+```
+
+## 基于路由的动态过渡
+
+也可以根据目标路由和当前路由之间的关系,动态地确定使用的过渡。使用和刚才非常相似的片段:
+
+```html
+<!-- 使用动态过渡名称 -->
+<router-view v-slot="{ Component, route }">
+  <transition :name="route.meta.transition">
+    <component :is="Component" />
+  </transition>
+</router-view>
+```
+
+我们可以添加一个 [after navigation hook](./navigation-guards.md#全局后置钩子),根据路径的深度动态添加信息到 `meta` 字段。
+
+```js
+router.afterEach((to, from) => {
+  const toDepth = to.path.split('/').length
+  const fromDepth = from.path.split('/').length
+  to.meta.transition = toDepth < fromDepth ? 'slide-right' : 'slide-left'
+})
+```
+
+## 强制在复用的视图之间进行过渡
+
+Vue 可能会自动复用看起来相似的组件,从而忽略了任何过渡。幸运的是,可以[添加一个 `key` 属性](https://v3.vuejs.org/api/special-attributes.html#key)来强制过渡。这也允许你在相同路由上使用不同的参数触发过渡:
+
+```vue
+<router-view v-slot="{ Component, route }">
+  <transition name="fade">
+    <component :is="Component" :key="route.path" />
+  </transition>
+</router-view>
+```
+
+<!-- TODO: interactive example -->
+<!-- See full example [here](https://github.com/vuejs/vue-router/blob/dev/examples/transitions/app.js). -->
diff --git a/packages/docs/zh/guide/essentials/dynamic-matching.md b/packages/docs/zh/guide/essentials/dynamic-matching.md
new file mode 100644 (file)
index 0000000..7d0646f
--- /dev/null
@@ -0,0 +1,125 @@
+# 带参数的动态路由匹配
+
+<VueSchoolLink
+  href="https://vueschool.io/lessons/dynamic-routes"
+  title="Learn about dynamic route matching with params"
+/>
+
+很多时候,我们需要将给定匹配模式的路由映射到同一个组件。例如,我们可能有一个 `User` 组件,它应该对所有用户进行渲染,但用户 ID 不同。在 Vue Router 中,我们可以在路径中使用一个动态字段来实现,我们称之为 _路径参数_ :
+
+```js
+const User = {
+  template: '<div>User</div>',
+}
+
+// 这些都会传递给 `createRouter`
+const routes = [
+  // 动态字段以冒号开始
+  { path: '/users/:id', component: User },
+]
+```
+
+现在像 `/users/johnny` 和 `/users/jolyne` 这样的 URL 都会映射到同一个路由。
+
+_路径参数_ 用冒号 `:` 表示。当一个路由被匹配时,它的 _params_ 的值将在每个组件中以 `this.$route.params` 的形式暴露出来。因此,我们可以通过更新 `User` 的模板来呈现当前的用户 ID:
+
+```js
+const User = {
+  template: '<div>User {{ $route.params.id }}</div>',
+}
+```
+
+你可以在同一个路由中设置有多个 _路径参数_,它们会映射到 `$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)中查看完整的细节。
+
+这个例子的 demo 可以在[这里](https://codesandbox.io/s/route-params-vue-router-examples-mlb14?from-embed&initialpath=%2Fusers%2Feduardo%2Fposts%2F1)找到。
+
+<!-- <iframe
+  src="https://codesandbox.io/embed//route-params-vue-router-examples-mlb14?fontsize=14&theme=light&view=preview&initialpath=%2Fusers%2Feduardo%2Fposts%2F1"
+  style="width:100%; height:500px; border:0; border-radius: 4px; overflow:hidden;"
+  title="Route Params example"
+  allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking"
+  sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
+></iframe> -->
+
+## 响应路由参数的变化
+
+<VueSchoolLink
+  href="https://vueschool.io/lessons/reacting-to-param-changes"
+  title="Learn how to react to param changes"
+/>
+
+使用带有参数的路由时需要注意的是,当用户从 `/users/johnny` 导航到 `/users/jolyne` 时,**相同的组件实例将被重复使用**。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。**不过,这也意味着组件的生命周期钩子不会被调用**。
+
+要对同一个组件中参数的变化做出响应的话,你可以简单地 watch `$route` 对象上的任意属性,在这个场景中,就是 `$route.params` :
+
+```js
+const User = {
+  template: '...',
+  created() {
+    this.$watch(
+      () => this.$route.params,
+      (toParams, previousParams) => {
+        // 对路由变化做出响应...
+      }
+    )
+  },
+}
+```
+
+或者,使用 `beforeRouteUpdate` [导航守卫](../advanced/navigation-guards.md),它也可以取消导航:
+
+```js
+const User = {
+  template: '...',
+  async beforeRouteUpdate(to, from) {
+    // 对路由变化做出响应...
+    this.userData = await fetchUser(to.params.id)
+  },
+}
+```
+
+## 捕获所有路由或 404 Not found 路由
+
+<VueSchoolLink
+  href="https://vueschool.io/lessons/404-not-found-page"
+  title="Learn how to make a catch all/404 not found route"
+/>
+
+常规参数只匹配 url 片段之间的字符,用 `/` 分隔。如果我们想匹配**任意路径**,我们可以使用自定义的 _路径参数_ 正则表达式,在 _路径参数_ 后面的括号中加入 正则表达式 :
+
+```js
+const routes = [
+  // 将匹配所有内容并将其放在 `$route.params.pathMatch` 下
+  { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
+  // 将匹配以 `/user-` 开头的所有内容,并将其放在 `$route.params.afterUser` 下
+  { path: '/user-:afterUser(.*)', component: UserGeneric },
+]
+```
+
+在这个特定的场景中,我们在括号之间使用了[自定义正则表达式](./route-matching-syntax.md#在参数中自定义正则),并将`pathMatch` 参数标记为[可选可重复](./route-matching-syntax.md#可选参数)。这样做是为了让我们在需要的时候,可以通过将 `path` 拆分成一个数组,直接导航到路由:
+
+```js
+this.$router.push({
+  name: 'NotFound',
+  // 保留当前路径并删除第一个字符,以避免目标 URL 以 `//` 开头。
+  params: { pathMatch: this.$route.path.substring(1).split('/') },
+  // 保留现有的查询和 hash 值,如果有的话
+  query: this.$route.query,
+  hash: this.$route.hash,
+})
+```
+
+更多内容请参见[重复参数](./route-matching-syntax.md#可重复的参数)部分。
+
+如果你正在使用[历史模式](./history-mode.md),请务必按照说明正确配置你的服务器。
+
+## 高级匹配模式
+
+Vue Router 使用自己的路径匹配语法,其灵感来自于 `express`,因此它支持许多高级匹配模式,如可选的参数,零或多个 / 一个或多个,甚至自定义的正则匹配规则。请查看[高级匹配](./route-matching-syntax.md)文档来探索它们。
diff --git a/packages/docs/zh/guide/essentials/history-mode.md b/packages/docs/zh/guide/essentials/history-mode.md
new file mode 100644 (file)
index 0000000..5a6e2eb
--- /dev/null
@@ -0,0 +1,200 @@
+# 不同的历史模式
+
+<VueSchoolLink
+  href="https://vueschool.io/lessons/history-mode"
+  title="Learn about the differences between Hash Mode and HTML5 Mode"
+/>
+
+在创建路由器实例时,`history` 配置允许我们在不同的历史模式中进行选择。
+
+## Hash 模式
+
+hash 模式是用 `createWebHashHistory()` 创建的:
+
+```js
+import { createRouter, createWebHashHistory } from 'vue-router'
+
+const router = createRouter({
+  history: createWebHashHistory(),
+  routes: [
+    //...
+  ],
+})
+```
+
+它在内部传递的实际 URL 之前使用了一个哈希字符(`#`)。由于这部分 URL 从未被发送到服务器,所以它不需要在服务器层面上进行任何特殊处理。不过,**它在 SEO 中确实有不好的影响**。如果你担心这个问题,可以使用 HTML5 模式。
+
+## HTML5 模式
+
+用 `createWebHistory()` 创建 HTML5 模式,推荐使用这个模式:
+
+```js
+import { createRouter, createWebHistory } from 'vue-router'
+
+const router = createRouter({
+  history: createWebHistory(),
+  routes: [
+    //...
+  ],
+})
+```
+
+当使用这种历史模式时,URL 会看起来很 "正常",例如 `https://example.com/user/id`。漂亮!
+
+不过,问题来了。由于我们的应用是一个单页的客户端应用,如果没有适当的服务器配置,用户在浏览器中直接访问 `https://example.com/user/id`,就会得到一个 404 错误。这就尴尬了。
+
+不用担心:要解决这个问题,你需要做的就是在你的服务器上添加一个简单的回退路由。如果 URL 不匹配任何静态资源,它应提供与你的应用程序中的 `index.html` 相同的页面。漂亮依旧!
+
+## 服务器配置示例
+
+**注意**:以下示例假定你正在从根目录提供服务。如果你部署到子目录,你应该使用[Vue CLI 的 `publicPath` 配置](https://cli.vuejs.org/config/#publicpath)和相关的[路由器的 `base` 属性](../../api/#createwebhistory)。你还需要调整下面的例子,以使用子目录而不是根目录(例如,将`RewriteBase/` 替换为 `RewriteBase/name-of-your-subfolder/`)。
+
+### Apache
+
+```apacheconf
+<IfModule mod_negotiation.c>
+  Options -MultiViews
+</IfModule>
+
+<IfModule mod_rewrite.c>
+  RewriteEngine On
+  RewriteBase /
+  RewriteRule ^index\.html$ - [L]
+  RewriteCond %{REQUEST_FILENAME} !-f
+  RewriteCond %{REQUEST_FILENAME} !-d
+  RewriteRule . /index.html [L]
+</IfModule>
+```
+
+也可以使用 [`FallbackResource`](https://httpd.apache.org/docs/2.2/mod/mod_dir.html#fallbackresource) 代替 `mod_rewrite`。
+
+### nginx
+
+```nginx
+location / {
+  try_files $uri $uri/ /index.html;
+}
+```
+
+### 原生 Node.js
+
+```js
+const http = require('http')
+const fs = require('fs')
+const httpPort = 80
+
+http
+  .createServer((req, res) => {
+    fs.readFile('index.html', 'utf-8', (err, content) => {
+      if (err) {
+        console.log('We cannot open "index.html" file.')
+      }
+
+      res.writeHead(200, {
+        'Content-Type': 'text/html; charset=utf-8',
+      })
+
+      res.end(content)
+    })
+  })
+  .listen(httpPort, () => {
+    console.log('Server listening on: http://localhost:%s', httpPort)
+  })
+```
+
+### Express + Node.js
+
+对于 Node.js/Express,可以考虑使用 [connect-history-api-fallback 中间件](https://github.com/bripkens/connect-history-api-fallback)。
+
+### Internet Information Services (IIS)
+
+1. 安装 [IIS UrlRewrite](https://www.iis.net/downloads/microsoft/url-rewrite)
+2. 在网站的根目录下创建一个 `web.config` 文件,内容如下:
+
+```xml
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+  <system.webServer>
+    <rewrite>
+      <rules>
+        <rule name="Handle History Mode and custom 404/500" stopProcessing="true">
+          <match url="(.*)" />
+          <conditions logicalGrouping="MatchAll">
+            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
+            <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
+          </conditions>
+          <action type="Rewrite" url="/" />
+        </rule>
+      </rules>
+    </rewrite>
+  </system.webServer>
+</configuration>
+```
+
+### Caddy v2
+
+```
+try_files {path} /
+```
+
+### Caddy v1
+
+```
+rewrite {
+    regexp .*
+    to {path} /
+}
+```
+
+### Firebase hosting
+
+将此添加到你的 `firebase.json` 中:
+
+```json
+{
+  "hosting": {
+    "public": "dist",
+    "rewrites": [
+      {
+        "source": "**",
+        "destination": "/index.html"
+      }
+    ]
+  }
+}
+```
+
+### Netlify
+
+创建一个 `_redirects` 文件,包含在你的部署文件中:
+
+```
+/* /index.html 200
+```
+
+在 vue-cli、nuxt 和 vite 项目中,这个文件通常放在名为 `static` 或 `public` 的目录下。
+
+你可以在 [Netlify 文档](https://docs.netlify.com/routing/redirects/rewrites-proxies/#history-pushstate-and-single-page-apps)中找到更多关于语法的信息。你也可以[创建一个 `netlify.toml`](https://docs.netlify.com/configure-builds/file-based-configuration/) 来结合其他 Netlify 功能的重定向。
+
+### Vercel
+
+在项目根目录创建一个`vercel.json`文件,内容如下:
+
+```json
+{
+  "rewrites": [{ "source": "/:path*", "destination": "/index.html" }]
+}
+```
+
+## Caveat
+
+这有一个注意事项。你的服务器将不再报告 404 错误,因为现在所有未找到的路径都会显示你的 `index.html` 文件。为了解决这个问题,你应该在你的 Vue 应用程序中实现一个万能的路由来显示 404 页面。
+
+```js
+const router = createRouter({
+  history: createWebHistory(),
+  routes: [{ path: '/:pathMatch(.*)', component: NotFoundComponent }],
+})
+```
+
+另外,如果你使用的是 Node.js 服务器,你可以通过在服务器端使用路由器来匹配传入的 URL,如果没有匹配到路由,则用 404 来响应,从而实现回退。查看 [Vue 服务器端渲染文档](https://v3.cn.vuejs.org/guide/ssr/introduction.html#what-is-server-side-rendering-ssr)了解更多信息。
diff --git a/packages/docs/zh/guide/essentials/named-routes.md b/packages/docs/zh/guide/essentials/named-routes.md
new file mode 100644 (file)
index 0000000..0bdd4de
--- /dev/null
@@ -0,0 +1,41 @@
+# 命名路由
+
+<VueSchoolLink
+  href="https://vueschool.io/lessons/named-routes"
+  title="Learn about the named routes"
+/>
+
+除了 `path` 之外,你还可以为任何路由提供 `name`。这有以下优点:
+
+- 没有硬编码的 URL
+- `params` 的自动编码/解码。
+- 防止你在 url 中出现打字错误。
+- 绕过路径排序(如显示一个)
+
+```js
+const routes = [
+  {
+    path: '/user/:username',
+    name: 'user',
+    component: User,
+  },
+]
+```
+
+要链接到一个命名的路由,可以向 `router-link` 组件的 `to` 属性传递一个对象:
+
+```html
+<router-link :to="{ name: 'user', params: { username: 'erina' }}">
+  User
+</router-link>
+```
+
+这跟代码调用 `router.push()` 是一回事:
+
+```js
+router.push({ name: 'user', params: { username: 'erina' } })
+```
+
+在这两种情况下,路由将导航到路径 `/user/erina`。
+
+完整的例子在[这里](https://github.com/vuejs/vue-router/blob/dev/examples/named-routes/app.js).
diff --git a/packages/docs/zh/guide/essentials/named-views.md b/packages/docs/zh/guide/essentials/named-views.md
new file mode 100644 (file)
index 0000000..11e8f5f
--- /dev/null
@@ -0,0 +1,92 @@
+# 命名视图
+
+<VueSchoolLink
+  href="https://vueschool.io/lessons/vue-router-4-named-views"
+  title="Learn how to use named views"
+/>
+
+有时候想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有 `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>
+```
+
+一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。确保正确使用 `components` 配置 (带上 **s**):
+
+```js
+const router = createRouter({
+  history: createWebHashHistory(),
+  routes: [
+    {
+      path: '/',
+      components: {
+        default: Home,
+        // LeftSidebar: LeftSidebar 的缩写
+        LeftSidebar,
+        // 它们与 `<router-view>` 上的 `name` 属性匹配
+        RightSidebar,
+      },
+    },
+  ],
+})
+```
+
+以上案例相关的可运行代码请[移步这里](https://codesandbox.io/s/named-views-vue-router-4-examples-rd20l).
+
+## 嵌套命名视图
+
+我们也有可能使用命名视图创建嵌套视图的复杂布局。这时你也需要命名用到的嵌套 `router-view` 组件。我们以一个设置面板为例:
+
+```
+/settings/emails                                       /settings/profile
++-----------------------------------+                  +------------------------------+
+| UserSettings                      |                  | UserSettings                 |
+| +-----+-------------------------+ |                  | +-----+--------------------+ |
+| | Nav | UserEmailsSubscriptions | |  +------------>  | | Nav | UserProfile        | |
+| |     +-------------------------+ |                  | |     +--------------------+ |
+| |     |                         | |                  | |     | UserProfilePreview | |
+| +-----+-------------------------+ |                  | +-----+--------------------+ |
++-----------------------------------+                  +------------------------------+
+```
+
+- `Nav` 只是一个常规组件。
+- `UserSettings` 是一个视图组件。
+- `UserEmailsSubscriptions`、`UserProfile`、`UserProfilePreview` 是嵌套的视图组件。
+
+**注意**:_我们先忘记 HTML/CSS 具体的布局的样子,只专注在用到的组件上。_
+
+`UserSettings` 组件的 `<template>` 部分应该是类似下面的这段代码:
+
+```html
+<!-- UserSettings.vue -->
+<div>
+  <h1>User Settings</h1>
+  <NavBar />
+  <router-view />
+  <router-view name="helper" />
+</div>
+```
+
+那么你就可以通过这个路由配置来实现上面的布局:
+
+```js
+{
+  path: '/settings',
+  // 你也可以在顶级路由就配置命名视图
+  component: UserSettings,
+  children: [{
+    path: 'emails',
+    component: UserEmailsSubscriptions
+  }, {
+    path: 'profile',
+    components: {
+      default: UserProfile,
+      helper: UserProfilePreview
+    }
+  }]
+}
+```
+
+以上案例相关的可运行代码请[移步这里](https://codesandbox.io/s/nested-named-views-vue-router-4-examples-re9yl?&initialpath=%2Fsettings%2Femails).
diff --git a/packages/docs/zh/guide/essentials/navigation.md b/packages/docs/zh/guide/essentials/navigation.md
new file mode 100644 (file)
index 0000000..82b1520
--- /dev/null
@@ -0,0 +1,113 @@
+---
+sidebarDepth: 0
+---
+
+# 编程式导航
+
+<VueSchoolLink
+  href="https://vueschool.io/lessons/vue-router-4-programmatic-navigation"
+  title="Learn how to navigate programmatically"
+/>
+
+除了使用 `<router-link>` 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。
+
+## 导航到不同的位置
+
+**注意:在 Vue 实例中,你可以通过 `$router` 访问路由实例。因此你可以调用 `this.$router.push`。**
+
+想要导航到不同的 URL,可以使用 `router.push` 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,会回到之前的 URL。
+
+当你点击 `<router-link>` 时,内部会调用这个方法,所以点击 `<router-link :to="...">` 相当于调用 `router.push(...)` :
+
+| 声明式                    | 编程式             |
+| ------------------------- | ------------------ |
+| `<router-link :to="...">` | `router.push(...)` |
+
+该方法的参数可以是一个字符串路径,或者一个描述地址的对象。例如:
+
+```js
+// 字符串路径
+router.push('/users/eduardo')
+
+// 带有路径的对象
+router.push({ path: '/users/eduardo' })
+
+// 命名的路由,并加上参数,让路由建立 url
+router.push({ name: 'user', params: { username: 'eduardo' } })
+
+// 带查询参数,结果是 /register?plan=private
+router.push({ path: '/register', query: { plan: 'private' } })
+
+// 带 hash,结果是 /about#team
+router.push({ path: '/about', hash: '#team' })
+```
+
+**注意**:如果提供了 `path`,`params` 会被忽略,上述例子中的 `query` 并不属于这种情况。取而代之的是下面例子的做法,你需要提供路由的 `name` 或手写完整的带有参数的 `path` :
+
+```js
+const username = 'eduardo'
+// 我们可以手动建立 url,但我们必须自己处理编码
+router.push(`/user/${username}`) // -> /user/eduardo
+// 同样
+router.push({ path: `/user/${username}` }) // -> /user/eduardo
+// 如果可能的话,使用 `name` 和 `params` 从自动 URL 编码中获益
+router.push({ name: 'user', params: { username } }) // -> /user/eduardo
+// `params` 不能与 `path` 一起使用
+router.push({ path: '/user', params: { username } }) // -> /user
+```
+
+当指定 `params` 时,可提供 `string` 或 `number` 参数(或者对于[可重复的参数](./route-matching-syntax.md#repeatable-params)可提供一个数组)。**任何其他类型(如 `undefined`、`false` 等)都将被自动字符串化**。对于[可选参数](./route-matching-syntax.md#repeatable-params),你可以提供一个空字符串(`""`)来跳过它。
+
+由于属性 `to` 与 `router.push` 接受的对象种类相同,所以两者的规则完全相同。
+
+`router.push` 和所有其他导航方法都会返回一个 _Promise_,让我们可以等到导航完成后才知道是成功还是失败。我们将在 [Navigation Handling](../advanced/navigation-failures.md) 中详细介绍。
+
+## 替换当前位置
+
+它的作用类似于 `router.push`,唯一不同的是,它在导航时不会向 history 添加新记录,正如它的名字所暗示的那样——它取代了当前的条目。
+
+| 声明式                            | 编程式                |
+| --------------------------------- | --------------------- |
+| `<router-link :to="..." replace>` | `router.replace(...)` |
+
+也可以直接在传递给 `router.push` 的 `routeLocation` 中增加一个属性 `replace: true` :
+
+```js
+router.push({ path: '/home', replace: true })
+// 相当于
+router.replace({ path: '/home' })
+```
+
+## 横跨历史
+
+<VueSchoolLink
+  href="https://vueschool.io/lessons/go-back"
+  title="Learn how to use Vue Router to go back"
+/>
+
+该方法采用一个整数作为参数,表示在历史堆栈中前进或后退多少步,类似于 `window.history.go(n)`。
+
+例子
+
+```js
+// 向前移动一条记录,与 router.forward() 相同
+router.go(1)
+
+// 返回一条记录,与 router.back() 相同
+router.go(-1)
+
+// 前进 3 条记录
+router.go(3)
+
+// 如果没有那么多记录,静默失败
+router.go(-100)
+router.go(100)
+```
+
+## 篡改历史
+
+你可能已经注意到,`router.push`、`router.replace` 和 `router.go` 是 [`window.history.pushState`、`window.history.replaceState` 和 `window.history.go`](https://developer.mozilla.org/en-US/docs/Web/API/History) 的翻版,它们确实模仿了 `window.history` 的 API。
+
+因此,如果你已经熟悉 [Browser History APIs](https://developer.mozilla.org/en-US/docs/Web/API/History_API),在使用 Vue Router 时,操作历史记录就会觉得很熟悉。
+
+值得一提的是,无论在创建路由器实例时传递什么样的 [`history` 配置](../../api/#history),Vue Router 的导航方法( `push`、`replace`、`go` )都能始终正常工作。
diff --git a/packages/docs/zh/guide/essentials/nested-routes.md b/packages/docs/zh/guide/essentials/nested-routes.md
new file mode 100644 (file)
index 0000000..84fa308
--- /dev/null
@@ -0,0 +1,130 @@
+# 嵌套路由
+
+<VueSchoolLink
+  href="https://vueschool.io/lessons/nested-routes"
+  title="Learn about nested routes"
+/>
+
+一些应用程序的 UI 由多层嵌套的组件组成。在这种情况下,URL 的片段通常对应于特定的嵌套组件结构,例如:
+
+```
+/user/johnny/profile                     /user/johnny/posts
++------------------+                  +-----------------+
+| User             |                  | User            |
+| +--------------+ |                  | +-------------+ |
+| | Profile      | |  +------------>  | | Posts       | |
+| |              | |                  | |             | |
+| +--------------+ |                  | +-------------+ |
++------------------+                  +-----------------+
+```
+
+通过 Vue Router,你可以使用嵌套路由配置来表达这种关系。
+
+接着上节创建的 app :
+
+```html
+<div id="app">
+  <router-view></router-view>
+</div>
+```
+
+```js
+const User = {
+  template: '<div>User {{ $route.params.id }}</div>',
+}
+
+// 这些都会传递给 `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>
+  `,
+}
+```
+
+要将组件渲染到这个嵌套的 `router-view` 中,我们需要在路由中配置 `children`:
+
+```js
+const routes = [
+  {
+    path: '/user/:id',
+    component: User,
+    children: [
+      {
+        // 当 /user/:id/profile 匹配成功
+        // UserProfile 将被渲染到 User 的 <router-view> 内部
+        path: 'profile',
+        component: UserProfile,
+      },
+      {
+        // 当 /user/:id/posts 匹配成功
+        // UserPosts 将被渲染到 User 的 <router-view> 内部
+        path: 'posts',
+        component: UserPosts,
+      },
+    ],
+  },
+]
+```
+
+**注意,以 `/` 开头的嵌套路径将被视为根路径。这允许你利用组件嵌套,而不必使用嵌套的 URL。**
+
+如你所见,`children` 配置只是另一个路由数组,就像 `routes` 本身一样。因此,你可以根据自己的需要,不断地嵌套视图。
+
+此时,按照上面的配置,当你访问 `/user/eduardo` 时,在 `User` 的 `router-view` 里面什么都不会呈现,因为没有匹配到嵌套路由。也许你确实想在那里渲染一些东西。在这种情况下,你可以提供一个空的嵌套路径:
+
+```js
+const routes = [
+  {
+    path: '/user/:id',
+    component: User,
+    children: [
+      // 当 /user/:id 匹配成功
+      // UserHome 将被渲染到 User 的 <router-view> 内部
+      { path: '', component: UserHome },
+
+      // ...其他子路由
+    ],
+  },
+]
+```
+
+这个例子的 demo 可以在[这里](https://codesandbox.io/s/nested-views-vue-router-4-examples-hl326?initialpath=%2Fusers%2Feduardo)找到。
+
+## 嵌套的命名路由
+
+在处理[命名路由](./named-routes.md)时,**你通常会给子路由命名**:
+
+```js
+const routes = [
+  {
+    path: '/user/:id',
+    component: User,
+    // 请注意,只有子路由具有名称
+    children: [{ path: '', name: 'user', component: UserHome }],
+  },
+]
+```
+
+这将确保导航到 `/user/:id` 时始终显示嵌套路由。
+
+在一些场景中,你可能希望导航到命名路由而不导航到嵌套路由。例如,你想导航 `/user/:id` 而不显示嵌套路由。那样的话,你还可以**命名父路由**,但请注意**重新加载页面将始终显示嵌套的子路由**,因为它被视为指向路径`/users/:id` 的导航,而不是命名路由:
+
+```js
+const routes = [
+  {
+    path: '/user/:id',
+    name: 'user-parent'
+    component: User,
+    children: [{ path: '', name: 'user', component: UserHome }],
+  },
+]
+```
diff --git a/packages/docs/zh/guide/essentials/passing-props.md b/packages/docs/zh/guide/essentials/passing-props.md
new file mode 100644 (file)
index 0000000..0cef876
--- /dev/null
@@ -0,0 +1,80 @@
+# 将 props 传递给路由组件
+
+<VueSchoolLink
+  href="https://vueschool.io/lessons/route-props"
+  title="Learn how to pass props to route components"
+/>
+
+在你的组件中使用 `$route` 会与路由紧密耦合,这限制了组件的灵活性,因为它只能用于特定的 URL。虽然这不一定是件坏事,但我们可以通过 `props` 配置来解除这种行为:
+
+我们可以将下面的代码
+
+```js
+const User = {
+  template: '<div>User {{ $route.params.id }}</div>'
+}
+const routes = [{ path: '/user/:id', component: User }]
+```
+
+替换成
+
+```js
+const User = {
+  // 请确保添加一个与路由参数完全相同的 prop 名
+  props: ['id'],
+  template: '<div>User {{ id }}</div>'
+}
+const routes = [{ path: '/user/:id', component: User, props: true }]
+```
+
+这允许你在任何地方使用该组件,使得该组件更容易重用和测试。
+
+## 布尔模式
+
+当 `props` 设置为 `true` 时,`route.params` 将被设置为组件的 props。
+
+## 命名视图
+
+对于有命名视图的路由,你必须为每个命名视图定义 `props` 配置:
+
+```js
+const routes = [
+  {
+    path: '/user/:id',
+    components: { default: User, sidebar: Sidebar },
+    props: { default: true, sidebar: false }
+  }
+]
+```
+
+## 对象模式
+
+当 `props` 是一个对象时,它将原样设置为组件 props。当 props 是静态的时候很有用。
+
+```js
+const routes = [
+  {
+    path: '/promotion/from-newsletter',
+    component: Promotion,
+    props: { newsletterPopup: false }
+  }
+]
+```
+
+## 函数模式
+
+你可以创建一个返回 props 的函数。这允许你将参数转换为其他类型,将静态值与基于路由的值相结合等等。
+
+```js
+const routes = [
+  {
+    path: '/search',
+    component: SearchUser,
+    props: route => ({ query: route.query.q })
+  }
+]
+```
+
+URL `/search?q=vue` 将传递 `{query: 'vue'}` 作为 props 传给 `SearchUser` 组件。
+
+请尽可能保持 `props` 函数为无状态的,因为它只会在路由发生变化时起作用。如果你需要状态来定义 props,请使用包装组件,这样 vue 才可以对状态变化做出反应。
diff --git a/packages/docs/zh/guide/essentials/redirect-and-alias.md b/packages/docs/zh/guide/essentials/redirect-and-alias.md
new file mode 100644 (file)
index 0000000..aa28a0e
--- /dev/null
@@ -0,0 +1,113 @@
+# 重定向和别名
+
+<VueSchoolLink
+  href="https://vueschool.io/lessons/vue-router-4-redirect-and-alias"
+  title="Learn how to use redirect and alias"
+/>
+
+## 重定向
+
+重定向也是通过 `routes` 配置来完成,下面例子是从 `/home` 重定向到 `/`:
+
+```js
+const routes = [{ path: '/home', redirect: '/' }]
+```
+
+重定向的目标也可以是一个命名的路由:
+
+```js
+const routes = [{ path: '/home', redirect: { name: 'homepage' } }]
+```
+
+甚至是一个方法,动态返回重定向目标:
+
+```js
+const routes = [
+  {
+    // /search/screens -> /search?q=screens
+    path: '/search/:searchText',
+    redirect: to => {
+      // 方法接收目标路由作为参数
+      // return 重定向的字符串路径/路径对象
+      return { path: '/search', query: { q: to.params.searchText } }
+    },
+  },
+  {
+    path: '/search',
+    // ...
+  },
+]
+```
+
+请注意,**[导航守卫](../advanced/navigation-guards.md)并没有应用在跳转路由上,而仅仅应用在其目标上**。在上面的例子中,在 `/home` 路由中添加 `beforeEnter` 守卫不会有任何效果。
+
+在写 `redirect` 的时候,可以省略 `component` 配置,因为它从来没有被直接访问过,所以没有组件要渲染。唯一的例外是[嵌套路由](./nested-routes.md):如果一个路由记录有 `children` 和 `redirect` 属性,它也应该有 `component` 属性。
+
+### 相对重定向
+
+也可以重定向到相对位置:
+
+```js
+const routes = [
+  {
+    // 将总是把/users/123/posts重定向到/users/123/profile。
+    path: '/users/:id/posts',
+    redirect: to => {
+      // 该函数接收目标路由作为参数
+      // 相对位置不以`/`开头
+      // 或 { path: 'profile'}
+      return 'profile'
+    },
+  },
+]
+```
+
+## 别名
+
+重定向是指当用户访问 `/home` 时,URL 会被 `/` 替换,然后匹配成 `/`。那么什么是别名呢?
+
+**将 `/` 别名为 `/home`,意味着当用户访问 `/home` 时,URL 仍然是 `/home`,但会被匹配为用户正在访问 `/`。**
+
+上面对应的路由配置为:
+
+```js
+const routes = [{ path: '/', component: Homepage, alias: '/home' }]
+```
+
+通过别名,你可以自由地将 UI 结构映射到一个任意的 URL,而不受配置的嵌套结构的限制。使别名以 `/` 开头,以使嵌套路径中的路径成为绝对路径。你甚至可以将两者结合起来,用一个数组提供多个别名:
+
+```js
+const routes = [
+  {
+    path: '/users',
+    component: UsersLayout,
+    children: [
+      // 为这 3 个 URL 呈现 UserList
+      // - /users
+      // - /users/list
+      // - /people
+      { path: '', component: UserList, alias: ['/people', 'list'] },
+    ],
+  },
+]
+```
+
+如果你的路由有参数,请确保在任何绝对别名中包含它们:
+
+```js
+const routes = [
+  {
+    path: '/users/:id',
+    component: UsersByIdLayout,
+    children: [
+      // 为这 3 个 URL 呈现 UserDetails
+      // - /users/24
+      // - /users/24/profile
+      // - /24
+      { path: 'profile', component: UserDetails, alias: ['/:id', ''] },
+    ],
+  },
+]
+```
+
+**关于 SEO 的注意事项**: 使用别名时,一定要[定义规范链接](https://support.google.com/webmasters/answer/139066?hl=en).
diff --git a/packages/docs/zh/guide/essentials/route-matching-syntax.md b/packages/docs/zh/guide/essentials/route-matching-syntax.md
new file mode 100644 (file)
index 0000000..67be20e
--- /dev/null
@@ -0,0 +1,119 @@
+# 路由的匹配语法
+
+<VueSchoolLink
+  href="https://vueschool.io/lessons/vue-router-4-advanced-routes-matching-syntax"
+  title="Learn how to use advanced route routes' matching syntax"
+/>
+
+大多数应用都会使用 `/about` 这样的静态路由和 `/users/:userId` 这样的动态路由,就像我们刚才在[动态路由匹配](./dynamic-matching.md)中看到的那样,但是 Vue Router 可以提供更多的方式!
+
+:::tip
+为了简单起见,所有的路由**都省略了 `component` 属性**,只关注 `path` 值。
+:::
+
+## 在参数中自定义正则
+
+当定义像 `:userId` 这样的参数时,我们内部使用以下的正则 `([^/]+)` (至少有一个字符不是斜杠 `/` )来从 URL 中提取参数。这很好用,除非你需要根据参数的内容来区分两个路由。想象一下,两个路由 `/:orderId` 和 `/:productName`,两者会匹配完全相同的 URL,所以我们需要一种方法来区分它们。最简单的方法就是在路径中添加一个静态部分来区分它们:
+
+```js
+const routes = [
+  // 匹配 /o/3549
+  { path: '/o/:orderId' },
+  // 匹配 /p/books
+  { path: '/p/:productName' },
+]
+```
+
+但在某些情况下,我们并不想添加静态的 `/o` `/p` 部分。由于,`orderId` 总是一个数字,而 `productName` 可以是任何东西,所以我们可以在括号中为参数指定一个自定义的正则:
+
+```js
+const routes = [
+  // /:orderId -> 仅匹配数字
+  { path: '/:orderId(\\d+)' },
+  // /:productName -> 匹配其他任何内容
+  { path: '/:productName' },
+]
+```
+
+现在,转到 `/25` 将匹配 `/:orderId`,其他情况将会匹配 `/:productName`。`routes` 数组的顺序并不重要!
+
+:::tip
+确保**转义反斜杠( `\` )**,就像我们对 `\d` (变成`\\d`)所做的那样,在 JavaScript 中实际传递字符串中的反斜杠字符。
+:::
+
+## 可重复的参数
+
+如果你需要匹配具有多个部分的路由,如 `/first/second/third`,你应该用 `*`(0 个或多个)和 `+`(1 个或多个)将参数标记为可重复:
+
+```js
+const routes = [
+  // /:chapters ->  匹配 /one, /one/two, /one/two/three, 等
+  { path: '/:chapters+' },
+  // /:chapters -> 匹配 /, /one, /one/two, /one/two/three, 等
+  { path: '/:chapters*' },
+]
+```
+
+这将为你提供一个参数数组,而不是一个字符串,并且在使用命名路由时也需要你传递一个数组:
+
+```js
+// 给定 { path: '/:chapters*', name: 'chapters' },
+router.resolve({ name: 'chapters', params: { chapters: [] } }).href
+// 产生 /
+router.resolve({ name: 'chapters', params: { chapters: ['a', 'b'] } }).href
+// 产生 /a/b
+
+// 给定 { path: '/:chapters+', name: 'chapters' },
+router.resolve({ name: 'chapters', params: { chapters: [] } }).href
+// 抛出错误,因为 `chapters` 为空 
+```
+
+这些也可以通过在**右括号后**添加它们与自定义正则结合使用:
+
+```js
+const routes = [
+  // 仅匹配数字
+  // 匹配 /1, /1/2, 等
+  { path: '/:chapters(\\d+)+' },
+  // 匹配 /, /1, /1/2, 等
+  { path: '/:chapters(\\d+)*' },
+]
+```
+
+## Sensitive 与 strict 路由配置
+
+默认情况下,所有路由是不区分大小写的,并且能匹配带有或不带有尾部斜线的路由。例如,路由 `/users` 将匹配 `/users`、`/users/`、甚至 `/Users/`。这种行为可以通过 `strict` 和 `sensitive` 选项来修改,它们可以既可以应用在整个全局路由上,又可以应用于当前路由上:
+
+```js
+const router = createRouter({
+  history: createWebHistory(),
+  routes: [
+    // 将匹配 /users/posva 而非:
+    // - /users/posva/ 当 strict: true
+    // - /Users/posva 当 sensitive: true
+    { path: '/users/:id', sensitive: true },
+    // 将匹配 /users, /Users, 以及 /users/42 而非 /users/ 或 /users/42/
+    { path: '/users/:id?' },
+  ]
+  strict: true, // applies to all routes
+})
+```
+
+## 可选参数
+
+你也可以通过使用 `?` 修饰符(0 个或 1 个)将一个参数标记为可选:
+
+```js
+const routes = [
+  // 匹配 /users 和 /users/posva
+  { path: '/users/:userId?' },
+  // 匹配 /users 和 /users/42
+  { path: '/users/:userId(\\d+)?' },
+]
+```
+
+请注意,`*` 在技术上也标志着一个参数是可选的,但 `?` 参数不能重复。
+
+## 调试
+
+如果你需要探究你的路由是如何转化为正则的,以了解为什么一个路由没有被匹配,或者,报告一个 bug,你可以使用[路径排名工具](https://paths.esm.dev/?p=AAMeJSyAwR4UbFDAFxAcAGAIJXMAAA..#)。它支持通过 URL 分享你的路由。
diff --git a/packages/docs/zh/guide/index.md b/packages/docs/zh/guide/index.md
new file mode 100644 (file)
index 0000000..5c4232a
--- /dev/null
@@ -0,0 +1,100 @@
+# 入门
+
+<VueSchoolLink
+  href="https://vueschool.io/courses/vue-router-4-for-everyone"
+  title="Learn how to build powerful Single Page Applications with the Vue Router on Vue School"
+/>
+
+用 Vue + Vue Router 创建单页应用非常简单:通过 Vue.js,我们已经用组件组成了我们的应用。当加入 Vue Router 时,我们需要做的就是将我们的组件映射到路由上,让 Vue Router 知道在哪里渲染它们。下面是一个基本的例子:
+
+## HTML
+
+```html
+<script src="https://unpkg.com/vue@3"></script>
+<script src="https://unpkg.com/vue-router@4"></script>
+
+<div id="app">
+  <h1>Hello App!</h1>
+  <p>
+    <!--使用 router-link 组件进行导航 -->
+    <!--通过传递 `to` 来指定链接 -->
+    <!--`<router-link>` 将呈现一个带有正确 `href` 属性的 `<a>` 标签-->
+    <router-link to="/">Go to Home</router-link>
+    <router-link to="/about">Go to About</router-link>
+  </p>
+  <!-- 路由出口 -->
+  <!-- 路由匹配到的组件将渲染在这里 -->
+  <router-view></router-view>
+</div>
+```
+
+### `router-link`
+
+请注意,我们没有使用常规的 `a` 标签,而是使用一个自定义组件 `router-link` 来创建链接。这使得 Vue Router 可以在不重新加载页面的情况下更改 URL,处理 URL 的生成以及编码。我们将在后面看到如何从这些功能中获益。
+
+### `router-view`
+
+`router-view` 将显示与 url 对应的组件。你可以把它放在任何地方,以适应你的布局。
+
+## JavaScript
+
+```js
+// 1. 定义路由组件.
+// 也可以从其他文件导入
+const Home = { template: '<div>Home</div>' }
+const About = { template: '<div>About</div>' }
+
+// 2. 定义一些路由
+// 每个路由都需要映射到一个组件。
+// 我们后面再讨论嵌套路由。
+const routes = [
+  { path: '/', component: Home },
+  { path: '/about', component: About },
+]
+
+// 3. 创建路由实例并传递 `routes` 配置
+// 你可以在这里输入更多的配置,但我们在这里
+// 暂时保持简单
+const router = VueRouter.createRouter({
+  // 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
+  history: VueRouter.createWebHashHistory(),
+  routes, // `routes: routes` 的缩写
+})
+
+// 5. 创建并挂载根实例
+const app = Vue.createApp({})
+//确保 _use_ 路由实例使
+//整个应用支持路由。
+app.use(router)
+
+app.mount('#app')
+
+// 现在,应用已经启动了!
+```
+
+通过调用 `app.use(router)`,我们可以在任意组件中以 `this.$router` 的形式访问它,并且以 `this.$route` 的形式访问当前路由:
+
+```js
+// Home.vue
+export default {
+  computed: {
+    username() {
+      // 我们很快就会看到 `params` 是什么
+      return this.$route.params.username
+    },
+  },
+  methods: {
+    goToDashboard() {
+      if (isAuthenticated) {
+        this.$router.push('/dashboard')
+      } else {
+        this.$router.push('/login')
+      }
+    },
+  },
+}
+```
+
+要在 `setup` 函数中访问路由,请调用 `useRouter` 或 `useRoute` 函数。我们将在 [Composition API](./advanced/composition-api.md#在-setup-中访问路由和当前路由) 中了解更多信息。
+
+在整个文档中,我们会经常使用 `router` 实例,请记住,`this.$router` 与直接使用通过 `createRouter` 创建的 `router` 实例完全相同。我们使用 `this.$router` 的原因是,我们不想在每个需要操作路由的组件中都导入路由。
diff --git a/packages/docs/zh/guide/migration/index.md b/packages/docs/zh/guide/migration/index.md
new file mode 100644 (file)
index 0000000..554c9d1
--- /dev/null
@@ -0,0 +1,440 @@
+# 从 Vue2 迁移
+
+在 Vue Router API 从 v3(Vue2)到 v4(Vue3)的重写过程中,大部分的 Vue Router API 都没有变化,但是在迁移你的程序时,你可能会遇到一些破坏性的变化。本指南将帮助你了解为什么会发生这些变化,以及如何调整你的程序,使其与 Vue Router4 兼容。
+
+## 破坏性变化
+
+变化的顺序是按其用途排列的。因此,建议按照这个清单的顺序进行。
+
+### new Router 变成 createRouter
+
+Vue Router 不再是一个类,而是一组函数。现在你不用再写 `new Router()`,而是要调用 `createRouter`:
+
+```js
+// 以前是
+// import Router from 'vue-router'
+import { createRouter } from 'vue-router'
+
+const router = createRouter({
+  // ...
+})
+```
+
+### 新的 `history` 配置取代 `mode`
+
+`mode: 'history'` 配置已经被一个更灵活的 `history` 配置所取代。根据你使用的模式,你必须用适当的函数替换它:
+
+- `"history"`: `createWebHistory()`
+- `"hash"`: `createWebHashHistory()`
+- `"abstract"`: `createMemoryHistory()`
+
+下面是一个完整的代码段:
+
+```js
+import { createRouter, createWebHistory } from 'vue-router'
+// 还有 createWebHashHistory 和 createMemoryHistory
+
+createRouter({
+  history: createWebHistory(),
+  routes: [],
+})
+```
+
+在 SSR 上使用时,你需要手动传递相应的 history:
+
+```js
+// router.js
+let history = isServer ? createMemoryHistory() : createWebHistory()
+let router = createRouter({ routes, history })
+// 在你的 server-entry.js 中的某个地方
+router.push(req.url) // 请求 url
+router.isReady().then(() => {
+  // 处理请求
+})
+```
+
+**原因**:为未使用的 history 启用摇树,以及为高级用例(如原生解决方案)实现自定义 history。
+
+### 移动了 `base` 配置
+
+现在,`base` 配置被作为 `createWebHistory` (其他 history 也一样)的第一个参数传递:
+
+```js
+import { createRouter, createWebHistory } from 'vue-router'
+createRouter({
+  history: createWebHistory('/base-directory/'),
+  routes: [],
+})
+```
+
+### 删除了 `fallback` 属性
+
+创建路由时不再支持 `fallback` 属性:
+
+```diff
+-new VueRouter({
++createRouter({
+-  fallback: false,
+// other options...
+})
+```
+
+**原因**: Vue支持的所有浏览器都支持 [HTML5 History API](https://developer.mozilla.org/zh-CN/docs/Web/API/History_API),因此我们不再需要使用 `location.hash`,而可以直接使用 `history.pushState()`。
+
+### 删除了 `*`(星标或通配符)路由
+
+现在必须使用自定义的 regex 参数来定义所有路由(`*`、`/*`):
+
+```js
+const routes = [
+  // pathMatch 是参数的名称,例如,跳转到 /not/found 会得到
+  // { params: { pathMatch: ['not', 'found'] } }
+  // 这要归功于最后一个 *,意思是重复的参数,如果你
+  // 打算直接使用未匹配的路径名称导航到该路径,这是必要的
+  { path: '/:pathMatch(.*)*', name: 'not-found', component: NotFound },
+  // 如果你省略了最后的 `*`,在解析或跳转时,参数中的 `/` 字符将被编码
+  { path: '/:pathMatch(.*)', name: 'bad-not-found', component: NotFound },
+]
+// 如果使用命名路由,不好的例子:
+router.resolve({
+  name: 'bad-not-found',
+  params: { pathMatch: 'not/found' },
+}).href // '/not%2Ffound'
+// 好的例子:
+router.resolve({
+  name: 'not-found',
+  params: { pathMatch: ['not', 'found'] },
+}).href // '/not/found'
+```
+
+:::tip
+如果你不打算使用其名称直接跳转到未找到的路由,则无需为重复的参数添加 `*`。如果你调用 `router.push('/not/found/url')`,它将提供正确的 `pathMatch` 参数。
+:::
+
+**原因**:Vue Router 不再使用 `path-to-regexp`,而是实现了自己的解析系统,允许路由排序并实现动态路由。由于我们通常在每个项目中只添加一个通配符路由,所以支持 `*` 的特殊语法并没有太大的好处。参数的编码是跨路由的,无一例外,让事情更容易预测。
+
+### 将 `onReady` 改为 `isReady`
+
+现有的 `router.onReady()` 函数已被 `router.isReady()` 取代,该函数不接受任何参数并返回一个 Promise:
+
+```js
+// 将
+router.onReady(onSuccess, onError)
+// 替换成
+router.isReady().then(onSuccess).catch(onError)
+// 或者使用 await:
+try {
+  await router.isReady()
+  // 成功
+} catch (err) {
+  // 报错
+}
+```
+
+### `scrollBehavior` 的变化
+
+`scrollBehavior` 中返回的对象与 [`ScrollToOptions`](https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions) 类似:`x` 改名为 `left`,`y` 改名为 `top`。详见 [RFC](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0035-router-scroll-position.md)。
+
+**原因**:使该对象类似于 `ScrollToOptions`,以使其感觉更像原生 JS API,并有可能启用将来的新配置。
+
+### `<router-view>`、`<keep-alive>` 和 `<transition>`
+
+`transition` 和 `keep-alive` 现在必须通过 `v-slot` API 在 `RouterView` **内部**使用:
+
+```vue
+<router-view v-slot="{ Component }">
+  <transition>
+    <keep-alive>
+      <component :is="Component" />
+    </keep-alive>
+  </transition>
+</router-view>
+```
+
+**原因**: 这是一个必要的变化。详见 [related RFC](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0034-router-view-keep-alive-transitions.md).
+
+### 删除 `<router-link>` 中的 `append` 属性
+
+`<router-link>` 中的 `append` 属性已被删除。你可以手动将值设置到现有的 `path` 中:
+
+```html
+将
+<router-link to="child-route" append>to relative child</router-link>
+替换成
+<router-link :to="append($route.path, 'child-route')">
+  to relative child
+</router-link>
+```
+
+你必须在 _App_ 实例上定义一个全局的 `append` 函数:
+
+```js
+app.config.globalProperties.append = (path, pathToAppend) =>
+  path + (path.endsWith('/') ? '' : '/') + pathToAppend
+```
+
+**原因**:`append` 使用频率不高,用户可以很容易地实现。
+
+### 删除 `<router-link>` 中的 `event` 和 `tag` 属性
+
+`<router-link>` 中的 `event` 和 `tag` 属性都已被删除。你可以使用 [`v-slot` API](../../api/#router-link-s-v-slot) 来完全定制 `<router-link>`:
+
+```html
+将
+<router-link to="/about" tag="span" event="dblclick">About Us</router-link>
+替换成
+<router-link to="/about" custom v-slot="{ navigate }">
+  <span @click="navigate" @keypress.enter="navigate" role="link">About Us</span>
+</router-link>
+```
+
+**原因**:这些属性经常一起使用,以使用与 `<a>` 标签不同的东西,但这些属性是在 `v-slot` API 之前引入的,并且没有足够的使用,因此没有足够的理由为每个人增加 bundle 包的大小。
+
+### 删除 `<router-link>` 中的 `exact` 属性
+
+`exact` 属性已被删除,因为不再存在要修复的警告,所以你应该能够安全地删除它。但,有两件事你应该注意:
+
+- 路由现在是基于它们所代表的路由记录来激活的,而不是路由地址对象及其 `path`、`query` 和 `hash` 属性来激活的
+- 只匹配 `path` 部分,`query` 和 `hash` 不再考虑
+
+如果你想自定义这种行为,例如考虑到 `hash` 部分,你应该使用 [`v-slot` API](https://next.router.vuejs.org/api/#router-link-s-v-slot) 来扩展`<router-link>`。
+
+**原因**: 详见 [RFC about active matching](https://github.com/vuejs/rfcs/blob/master/active-rfcs/0028-router-active-link.md#summary)。
+
+### 忽略 mixins 中的导航守卫
+
+目前不支持 mixins 中的导航守卫,你可以在 [vue-router#454](https://github.com/vuejs/router/issues/454) 追踪它的支持情况。
+
+### 删除 `router.match` 改为 `router.resolve`
+
+`router.match` 和 `router.resolve` 已合并到 `router.resolve` 中,签名略有不同。[详见 API](../../api/#resolve)。
+
+**原因**:将用于同一目的的多种方法统一起来。
+
+### 删除 `router.getMatchedComponents()`
+
+`router.getMatchedComponents` 方法现在被删除,因为匹配的组件可以从 `router.currentRoute.value.matched` 中获取:
+
+```js
+router.currentRoute.value.matched.flatMap(record =>
+  Object.values(record.components)
+)
+```
+
+**原因**:这个方法只在 SSR 中使用,并且是用户一行就能完成的操作。
+
+### **所有**的导航现在都是异步的
+
+所有的导航,包括第一个导航,现在都是异步的,这意味着,如果你使用一个 `transition`,你可能需要等待路由 _ready_ 好后再挂载程序:
+
+```js
+app.use(router)
+// 注意:在服务器端,你需要手动跳转到初始地址。
+router.isReady().then(() => app.mount('#app'))
+```
+
+否则会有一个初始过渡,就像你提供了 `appear` 属性到 `transition` 一样,因为路由会显示它的初始地址(什么都没有),然后显示第一个地址。
+
+请注意,**如果在初始导航时有导航守卫**,你可能不想阻止程序渲染,直到它们被解析,除非你正在进行服务器端渲染。否则,在这种情况下,不等待路由准备好挂载应用会产生与 Vue2 中相同的结果。
+
+### 删除 `router.app`
+
+`router.app` 用于表示注入路由的最后一个根组件(Vue 实例)。Vue Router 现在可以被多个 Vue 程序同时安全使用。你仍然可以在使用路由时添加它:
+
+```js
+app.use(router)
+router.app = app
+```
+
+你也可以扩展 `Router` 接口的 TypeScript 定义来添加 `app` 属性。
+
+**原因**:Vue3 写的程序不能在 Vue2 中使用,现在我们使用同一个 Router 实例来支持多个程序,因此拥有 `app` 属性可能会产生误导,因为它是程序而不是根实例。
+
+### 将内容传递给路由组件的 `<slot>`
+
+之前你可以直接传递一个模板,通过嵌套在 `<router-view>` 组件下,由路由组件的 `<slot>` 来渲染:
+
+```html
+<router-view>
+  <p>In Vue Router 3, I render inside the route component</p>
+</router-view>
+```
+
+由于 `<router-view>` 引入了 `v-slot` API,你必须使用 `v-slot` API 将其传递给 `<component>`:
+
+```html
+<router-view v-slot="{ Component }">
+  <component :is="Component">
+    <p>In Vue Router 3, I render inside the route component</p>
+  </component>
+</router-view>
+```
+
+### 将 `parent` 从路由地址中删除
+
+`parent` 属性已从标准化路由地址(`this.$route` 和 `router.resolve` 返回的对象)中删除。你仍然可以通过 `matched` 数组访问它:
+
+```js
+const parent = this.$route.matched[this.$route.matched.length - 2]
+```
+
+**原因**:同时存在 `parent` 和 `children` 会造成不必要的循环引用,而属性可以通过 `matched` 来检索。
+
+### 删除 `pathToRegexpOptions`
+
+路由的 `pathToRegexpOptions` 和 `caseSensitive` 属性已被 `createRouter()` 的 `sensitive` 和 `strict` 配置取代。现在,当使用 `createRouter()` 创建路由时,它们也可以直接传递。`path-to-regexp` 的任何其他特定配置已被删除,因为 `path-to-regexp` 已不再用于解析路径。
+
+### 删除未命名的参数
+
+由于取消了 `path-to-regexp`,所以不再支持未命名的参数:
+
+- `/foo(/foo)?/suffix` 变成 `/foo/:_(foo)?/suffix`
+- `/foo(foo)?` 变成 `/foo:_(foo)?`
+- `/foo/(.*)` 变成 `/foo/:_(.*)`
+
+:::tip
+请注意,你可以使用任何名称代替 `_` 作为参数。重点是要提供一个名字。
+:::
+
+### `history.state` 的用法
+
+Vue Router 将信息保存在 `history.state` 上。如果你有任何手动调用 `history.pushState()` 的代码,你应该避免它,或者用的 `router.push()` 和 `history.replaceState()` 进行重构:
+
+```js
+// 将
+history.pushState(myState, '', url)
+// 替换成
+await router.push(url)
+history.replaceState({ ...history.state, ...myState }, '')
+```
+
+同样,如果你在调用 `history.replaceState()` 时没有保留当前状态,你需要传递当前 `history.state`:
+
+```js
+// 将
+history.replaceState({}, '', url)
+// 替换成
+history.replaceState(history.state, '', url)
+```
+
+**原因**:我们使用历史状态来保存导航信息,如滚动位置,以前的地址等。
+
+### `options` 中需要配置 `routes`
+
+`options` 中的 `routes` 属性现在是必需的。
+
+```js
+createRouter({ routes: [] })
+```
+
+**原因**:路由的设计是为了创建路由,尽管你可以在以后添加它们。在大多数情况下,你至少需要一条路由,一般每个应用都会编写一次。
+
+### 不存在的命名路由
+
+跳转或解析不存在的命名路由会产生错误:
+
+```js
+// 哎呀,我们的名字打错了
+router.push({ name: 'homee' }) // 报错
+router.resolve({ name: 'homee' }) // 报错
+```
+
+**原因**:以前,路由会导航到 `/`,但不显示任何内容(而不是主页)。抛出一个错误更有意义,因为我们不能生成一个有效的 URL 进行导航
+
+### 命名路由缺少必要的 `params`
+
+在没有传递所需参数的情况下跳转或解析命名路由,会产生错误:
+
+```js
+// 给与以下路由:
+const routes = [{ path: '/users/:id', name: 'user', component: UserDetails }]
+
+// 缺少 `id` 参数会失败
+router.push({ name: 'user' })
+router.resolve({ name: 'user' })
+```
+
+**原因**:同上。
+
+### 带有空 `path` 的命名子路由不再添加斜线
+
+给予任何空 `path` 的嵌套命名路由:
+
+```js
+const routes = [
+  {
+    path: '/dashboard',
+    name: 'dashboard-parent',
+    component: DashboardParent,
+    children: [
+      { path: '', name: 'dashboard', component: DashboardDefault },
+      {
+        path: 'settings',
+        name: 'dashboard-settings',
+        component: DashboardSettings,
+      },
+    ],
+  },
+]
+```
+
+现在,导航或解析到命名的路由 `dashboard` 时,会产生一个**不带斜线的** URL:
+
+```js
+router.resolve({ name: 'dashboard' }).href // '/dashboard'
+```
+
+这对子级 `redirect` 有重要的副作用,如下所示:
+
+```js
+const routes = [
+  {
+    path: '/parent',
+    component: Parent,
+    children: [
+      // 现在将重定向到 `/home` 而不是 `/parent/home`
+      { path: '', redirect: 'home' },
+      { path: 'home', component: Home },
+    ],
+  },
+]
+```
+
+请注意,如果 `path` 是 `/parent/`,这也可以,因为 `home` 到 `/parent/` 的相对地址确实是 `/parent/home`,但 `home` 到 `/parent` 的相对地址是 `/home`。
+
+<!-- Learn more about relative links [in the cookbook](/cookbook/relative-links.md). -->
+
+**原因**:这是为了使尾部的斜线行为保持一致:默认情况下,所有路由都允许使用尾部的斜线。可以通过使用 `strict` 配置和手动添加(或不添加)斜线来禁用它。
+
+<!-- TODO: maybe a cookbook entry -->
+
+### `$route` 属性编码
+
+无论在哪里启动导航,`params`、`query`和 `hash` 中的解码值现在都是一致的(旧的浏览器仍然会产生未编码的 `path` 和 `fullPath`)。初始导航应产生与应用内部导航相同的结果。
+
+给定任何[规范化的路由地址](../../api/#routelocationnormalized):
+
+- `path`, `fullPath`中的值不再被解码了。例如,直接在地址栏上写 "https://example.com/hello world",将得到编码后的版本:"https://example.com/hello%20world",而 "path "和 "fullPath "都是"/hello%20world"。
+- `hash` 现在被解码了,这样就可以复制过来。`router.push({ hash: $route.hash })` 可以直接用于 [scrollBehavior](../../api/#scrollbehavior) 的 `el` 配置中。
+- 当使用 `push`、`resolve` 和 `replace` 并在对象中提供 `string` 地址或 `path` 属性时,**必须进行编码**(像以前的版本一样)。另一方面,`params`、`query` 和 `hash` 必须以未编码的版本提供。
+- 斜线字符(`/`)现在已在 `params` 内正确解码,同时仍在 URL 上产生一个编码版本:`%2F`。
+
+**原因**:这样,在调用 `router.push()` 和 `router.resolve()` 时,可以很容易地复制一个地址的现有属性,并使产生的路由地址在各浏览器之间保持一致。`router.push()` 现在是幂等的,这意味着调用 `router.push(route.fullPath)`、`router.push({ hash: route.hash })`、`router.push({ query: route.query })` 和`router.push({ params: route.params })` 不会产生额外的编码。
+
+### TypeScript 变化
+
+为了使类型更一致,更有表现力,有些类型被重新命名:
+
+| `vue-router@3` | `vue-router@4`          |
+| -------------- | ----------------------- |
+| RouteConfig    | RouteRecordRaw          |
+| Location       | RouteLocation           |
+| Route          | RouteLocationNormalized |
+
+## 新功能
+
+Vue Router4 中需要关注的一些新功能包括:
+
+- [动态路由](../advanced/dynamic-routing.md)
+- [组合式 API](../advanced/composition-api.md)
+<!-- - Custom History implementation -->
diff --git a/packages/docs/zh/index.md b/packages/docs/zh/index.md
new file mode 100644 (file)
index 0000000..1a20e81
--- /dev/null
@@ -0,0 +1,45 @@
+---
+layout: home
+
+title: Vue Router
+titleTemplate: Vue.js 的官方路由
+
+hero: 
+  name: Vue Router
+  text: Vue.js 的官方路由
+  tagline: Expressive, configurable and convenient routing for Vue.js
+  image:
+    src: /logo.png
+    alt: Vue Router
+  actions:
+    - theme: brand
+      text: 入门 →
+      link: /zh/introduction
+    - theme: cta vueschool
+      text: 免费视频课程
+      link: https://vueschool.io/courses/vue-router-4-for-everyone?friend=vuerouter&utm_source=vuerouter&utm_medium=link&utm_campaign=homepage
+    - theme: cta vue-mastery
+      text: Get the Vue Router Cheat Sheet
+      link: https://www.vuemastery.com/vue-router?coupon=ROUTER-DOCS&via=eduardo
+
+features:
+  - title: 🛣 富有表现力的路由语法
+    details: 用直观且强大的语法来定义静态或动态路由。
+  - title: 🛑 细致的导航控制
+    details: 可拦截任何导航并更精确地控制其结果。
+  - title: 🧱 基于组件的配置方法
+    details: 将每条路由映射到应该显示的组件上。
+  - title: 🔌 支持历史模式
+    details: 有 HTML5、hash 或记忆历史模式可供选择。
+  - title: 🎚 支持滚动控制
+    details: 可精确控制每个页面的滚动位置。
+  - title: 🌐 支持自动编码
+    details: 可直接在代码中使用 unicode 字符(你好)。
+---
+
+<script setup>
+import HomeSponsors from '../.vitepress/theme/components/HomeSponsors.vue'
+import '../.vitepress/theme/styles/home-links.css'
+</script>
+
+<HomeSponsors />
diff --git a/packages/docs/zh/installation.md b/packages/docs/zh/installation.md
new file mode 100644 (file)
index 0000000..a3058e9
--- /dev/null
@@ -0,0 +1,23 @@
+# 安装
+
+## 直接下载 / 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-->
+
+## npm
+
+```bash
+npm install vue-router@4
+```
+
+## yarn
+
+```bash
+yarn add vue-router@4
+```
\ No newline at end of file
diff --git a/packages/docs/zh/introduction.md b/packages/docs/zh/introduction.md
new file mode 100644 (file)
index 0000000..d0fdc90
--- /dev/null
@@ -0,0 +1,27 @@
+# 介绍
+
+<VueSchoolLink
+  href="https://vueschool.io/courses/vue-router-4-for-everyone"
+  title="Learn how to build powerful Single Page Applications with the Vue Router on Vue School"
+/>
+
+Vue Router 是 [Vue.js](https://vuejs.org) 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举。功能包括:
+
+- 嵌套路由映射
+- 动态路由选择
+- 模块化、基于组件的路由配置
+- 路由参数、查询、通配符
+- 展示由 Vue.js 的过渡系统提供的过渡效果
+- 细致的导航控制
+- 自动激活 CSS 类的链接
+- HTML5 history 模式或 hash 模式
+- 可定制的滚动行为
+- URL 的正确编码
+
+[入门](./guide/)或使用 [playground](https://github.com/vuejs/router/tree/main/packages/playground) (详见[`README.md`](https://github.com/vuejs/router)来运行它们)。
+
+<HomeSponsors />
+
+<script setup>
+import HomeSponsors from '../.vitepress/theme/components/HomeSponsors.vue'
+</script>