]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
docs: update examples to use SFCs (#2174)
authorskirtle <65301168+skirtles-code@users.noreply.github.com>
Thu, 14 Mar 2024 06:34:10 +0000 (06:34 +0000)
committerGitHub <noreply@github.com>
Thu, 14 Mar 2024 06:34:10 +0000 (07:34 +0100)
* docs: update examples to use SFCs

* Further small docs tweaks

12 files changed:
packages/docs/guide/advanced/composition-api.md
packages/docs/guide/advanced/data-fetching.md
packages/docs/guide/advanced/dynamic-routing.md
packages/docs/guide/advanced/extending-router-link.md
packages/docs/guide/advanced/meta.md
packages/docs/guide/advanced/navigation-guards.md
packages/docs/guide/essentials/dynamic-matching.md
packages/docs/guide/essentials/named-views.md
packages/docs/guide/essentials/nested-routes.md
packages/docs/guide/essentials/passing-props.md
packages/docs/guide/index.md
packages/docs/installation.md

index 3e9213f7841e59db00b7f007d9180a8b5d9172e9..3a4fc64aa8d58192210d2f300548200a06f60154 100644 (file)
@@ -2,90 +2,84 @@
 
 <VueSchoolLink
   href="https://vueschool.io/lessons/router-and-the-composition-api"
-  title="Learn how to use Vue Router with the composition API"
+  title="Learn how to use Vue Router with the Composition API"
 />
 
-The introduction of `setup` and Vue's [Composition API](https://vuejs.org/guide/extras/composition-api-faq.html), open up new possibilities but to be able to get the full potential out of Vue Router, we will need to use a few new functions to replace access to `this` and in-component navigation guards.
+The introduction of Vue's [Composition API](https://vuejs.org/guide/extras/composition-api-faq.html) opened up new possibilities, but to be able to get the full potential out of Vue Router, we will need to use a few new functions to replace access to `this` and in-component navigation guards.
 
 ## Accessing the Router and current Route inside `setup`
 
-Because we don't have access to `this` inside of `setup`, we cannot directly access `this.$router` or `this.$route` anymore. Instead we use the `useRouter` and `useRoute` functions:
+Because we don't have access to `this` inside of `setup`, we cannot directly access `this.$router` or `this.$route`. Instead, we use the `useRouter` and `useRoute` composables:
 
-```js
+```vue
+<script setup>
 import { useRouter, useRoute } from 'vue-router'
 
-export default {
-  setup() {
-    const router = useRouter()
-    const route = useRoute()
-
-    function pushWithQuery(query) {
-      router.push({
-        name: 'search',
-        query: {
-          ...route.query,
-          ...query,
-        },
-      })
-    }
-  },
+const router = useRouter()
+const route = useRoute()
+
+function pushWithQuery(query) {
+  router.push({
+    name: 'search',
+    query: {
+      ...route.query,
+      ...query,
+    },
+  })
 }
+</script>
 ```
 
-The `route` object is a reactive object, so any of its properties can be watched and you should **avoid watching the whole `route`** object. In most scenarios, you should directly watch the param you are expecting to change
+The `route` object is a reactive object. In most scenarios, you should **avoid watching the whole `route`** object. Instead, you can directly watch the properties you are expecting to change:
 
-```js
+```vue
+<script setup>
 import { useRoute } from 'vue-router'
 import { ref, watch } from 'vue'
 
-export default {
-  setup() {
-    const route = useRoute()
-    const userData = ref()
-
-    // fetch the user information when params change
-    watch(
-      () => route.params.id,
-      async newId => {
-        userData.value = await fetchUser(newId)
-      }
-    )
-  },
-}
+const route = useRoute()
+const userData = ref()
+
+// fetch the user information when params change
+watch(
+  () => route.params.id,
+  async newId => {
+    userData.value = await fetchUser(newId)
+  }
+)
+</script>
 ```
 
-Note we still have access to `$router` and `$route` in templates, so there is no need to return `router` or `route` inside of `setup`.
+Note we still have access to `$router` and `$route` in templates, so there's no need to use `useRouter` or `useRoute` if we only need those object in the template.
 
 ## Navigation Guards
 
-While you can still use in-component navigation guards with a `setup` function, Vue Router exposes update and leave guards as Composition API functions:
+Vue Router exposes update and leave guards as Composition API functions:
 
-```js
+```vue
+<script setup>
 import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
 import { ref } from 'vue'
 
-export default {
-  setup() {
-    // same as beforeRouteLeave option with no access to `this`
-    onBeforeRouteLeave((to, from) => {
-      const answer = window.confirm(
-        'Do you really want to leave? you have unsaved changes!'
-      )
-      // cancel the navigation and stay on the same page
-      if (!answer) return false
-    })
-
-    const userData = ref()
-
-    // same as beforeRouteUpdate option with no access to `this`
-    onBeforeRouteUpdate(async (to, from) => {
-      // only fetch the user if the id changed as maybe only the query or the hash changed
-      if (to.params.id !== from.params.id) {
-        userData.value = await fetchUser(to.params.id)
-      }
-    })
-  },
-}
+// same as beforeRouteLeave option but with no access to `this`
+onBeforeRouteLeave((to, from) => {
+  const answer = window.confirm(
+    'Do you really want to leave? you have unsaved changes!'
+  )
+  // cancel the navigation and stay on the same page
+  if (!answer) return false
+})
+
+const userData = ref()
+
+// same as beforeRouteUpdate option but with no access to `this`
+onBeforeRouteUpdate(async (to, from) => {
+  // only fetch the user if the id changed as maybe only the query or the hash changed
+  if (to.params.id !== from.params.id) {
+    userData.value = await fetchUser(to.params.id)
+  }
+})
+</script>
 ```
 
 Composition API guards can also be used in any component rendered by `<router-view>`, they don't have to be used directly on the route component like in-component guards.
@@ -94,40 +88,34 @@ Composition API guards can also be used in any component rendered by `<router-vi
 
 Vue Router exposes the internal behavior of RouterLink as a composable. It accepts a reactive object like the props of `RouterLink` and exposes low-level properties to build your own `RouterLink` component or generate custom links:
 
-```js
+```vue
+<script setup>
 import { RouterLink, useLink } from 'vue-router'
 import { computed } from 'vue'
 
-export default {
-  name: 'AppLink',
-
-  props: {
-    // add @ts-ignore if using TypeScript
-    ...RouterLink.props,
-    inactiveClass: String,
-  },
-
-  setup(props) {
-    const {
-      // the resolved route object
-      route,
-      // the href to use in a link
-      href,
-      // boolean ref  indicating if the link is active
-      isActive,
-      // boolean ref  indicating if the link is exactly active
-      isExactActive,
-      // function to navigate to the link
-      navigate
-      } = useLink(props)
-
-    const isExternalLink = computed(
-      () => typeof props.to === 'string' && props.to.startsWith('http')
-    )
-
-    return { isExternalLink, href, navigate, isActive }
-  },
-}
+const props = defineProps({
+  // add @ts-ignore if using TypeScript
+  ...RouterLink.props,
+  inactiveClass: String,
+})
+
+const {
+  // the resolved route object
+  route,
+  // the href to use in a link
+  href,
+  // boolean ref indicating if the link is active
+  isActive,
+  // boolean ref indicating if the link is exactly active
+  isExactActive,
+  // function to navigate to the link
+  navigate
+} = useLink(props)
+
+const isExternalLink = computed(
+  () => typeof props.to === 'string' && props.to.startsWith('http')
+)
+</script>
 ```
 
 Note that the RouterLink's `v-slot` gives access to the same properties as the `useLink` composable.
index e0725abeb49e8d8c87787f818a21d7da51e9da50..c6fd1bfb41bd3680f445b3b6da9b4387489968c7 100644 (file)
@@ -10,11 +10,13 @@ Technically, both are valid choices - it ultimately depends on the user experien
 
 ## Fetching After Navigation
 
-When using this approach, we navigate and render the incoming component immediately, and fetch data in the component's `created` hook. It gives us the opportunity to display a loading state while the data is being fetched over the network, and we can also handle loading differently for each view.
+When using this approach, we navigate and render the incoming component immediately, and fetch data in the component itself. It gives us the opportunity to display a loading state while the data is being fetched over the network, and we can also handle loading differently for each view.
 
-Let's assume we have a `Post` component that needs to fetch the data for a post based on `$route.params.id`:
+Let's assume we have a `Post` component that needs to fetch the data for a post based on `route.params.id`:
 
-```html
+::: code-group
+
+```vue [Composition API]
 <template>
   <div class="post">
     <div v-if="loading" class="loading">Loading...</div>
@@ -27,9 +29,54 @@ Let's assume we have a `Post` component that needs to fetch the data for a post
     </div>
   </div>
 </template>
+
+<script setup>
+import { ref, watch } from 'vue'
+import { useRoute } from 'vue-router'
+import { getPost } from './api.js'
+
+const route = useRoute()
+
+const loading = ref(false)
+const post = ref(null)
+const error = ref(null)
+
+// watch the params of the route to fetch the data again
+watch(() => route.params.id, fetchData, { immediate: true })
+
+async function fetchData(id) {
+  error.value = post.value = null
+  loading.value = true
+  
+  try {
+    // replace `getPost` with your data fetching util / API wrapper
+    post.value = await getPost(id)  
+  } catch (err) {
+    error.value = err.toString()
+  } finally {
+    loading.value = false
+  }
+}
+</script>
 ```
 
-```js
+```vue [Options API]
+<template>
+  <div class="post">
+    <div v-if="loading" class="loading">Loading...</div>
+
+    <div v-if="error" class="error">{{ error }}</div>
+
+    <div v-if="post" class="content">
+      <h2>{{ post.title }}</h2>
+      <p>{{ post.body }}</p>
+    </div>
+  </div>
+</template>
+
+<script>
+import { getPost } from './api.js'
+
 export default {
   data() {
     return {
@@ -41,37 +88,37 @@ export default {
   created() {
     // watch the params of the route to fetch the data again
     this.$watch(
-      () => this.$route.params,
-      () => {
-        this.fetchData()
-      },
+      () => this.$route.params.id,
+      this.fetchData,
       // fetch the data when the view is created and the data is
       // already being observed
       { immediate: true }
     )
   },
   methods: {
-    fetchData() {
+    async fetchData(id) {
       this.error = this.post = null
       this.loading = true
-      // replace `getPost` with your data fetching util / API wrapper
-      getPost(this.$route.params.id, (err, post) => {
+
+      try {
+        // replace `getPost` with your data fetching util / API wrapper
+        this.post = await getPost(id)
+      } catch (err) {
+        this.error = err.toString()
+      } finally {
         this.loading = false
-        if (err) {
-          this.error = err.toString()
-        } else {
-          this.post = post
-        }
-      })
+      }
     },
   },
 }
+</script>
 ```
 
+:::
+
 ## Fetching Before Navigation
 
-With this approach we fetch the data before actually navigating to the new
-route. We can perform the data fetching in the `beforeRouteEnter` guard in the incoming component, and only call `next` when the fetch is complete. The callback passed to `next` will be called **after the component is mounted**:
+With this approach we fetch the data before actually navigating to the new route. We can perform the data fetching in the `beforeRouteEnter` guard in the incoming component, and only call `next` when the fetch is complete. The callback passed to `next` will be called **after the component is mounted**:
 
 ```js
 export default {
@@ -81,29 +128,28 @@ export default {
       error: null,
     }
   },
-  beforeRouteEnter(to, from, next) {
-    getPost(to.params.id, (err, post) => {
-      // `setData` is a method defined below
-      next(vm => vm.setData(err, post))
-    })
+  async beforeRouteEnter(to, from, next) {
+    try {
+      const post = await getPost(to.params.id)
+      // `setPost` is a method defined below
+      next(vm => vm.setPost(post))
+    } catch (err) {
+      // `setError` is a method defined below
+      next(vm => vm.setError(err))
+    }
   },
   // when route changes and this component is already rendered,
   // the logic will be slightly different.
-  async beforeRouteUpdate(to, from) {
+  beforeRouteUpdate(to, from) {
     this.post = null
-    try {
-      this.post = await getPost(to.params.id)
-    } catch (error) {
-      this.error = error.toString()
-    }
+    getPost(to.params.id).then(this.setPost).catch(this.setError)
   },
   methods: {
-    setData(error, post) {
-      if (error) {
-        this.error = error
-      } else {
-        this.post = post
-      }
+    setPost(post) {
+      this.post = post
+    },
+    setError(err) {
+      this.error = err.toString()
     }
   }
 }
index a2ad81a79d329eb7e3801c577e55497e3c670aa3..d8d83b792cded36e245a3aaccab91532ca0be3a3 100644 (file)
@@ -30,7 +30,7 @@ The page will still show the `Article` component, we need to manually call `rout
 
 ```js
 router.addRoute({ path: '/about', component: About })
-// we could also use this.$route or route = useRoute() (inside a setup)
+// we could also use this.$route or useRoute()
 router.replace(router.currentRoute.value.fullPath)
 ```
 
index 6b4ff25c49d8a137a793378ac4d58db48c658ea0..5102b9ad5303edf1aa032055060e765627d65e79 100644 (file)
@@ -9,7 +9,28 @@ The RouterLink component exposes enough `props` to suffice most basic applicatio
 
 Let's extend RouterLink to handle external links as well and adding a custom `inactive-class` in an `AppLink.vue` file:
 
-```vue
+::: code-group
+
+```vue [Composition API]
+<script setup>
+import { computed } from 'vue'
+import { RouterLink } from 'vue-router'
+
+defineOptions({
+  inheritAttrs: false,
+})
+
+const props = defineProps({
+  // add @ts-ignore if using TypeScript
+  ...RouterLink.props,
+  inactiveClass: String,
+})
+
+const isExternalLink = computed(() => {
+  return typeof props.to === 'string' && props.to.startsWith('http')
+})
+</script>
+
 <template>
   <a v-if="isExternalLink" v-bind="$attrs" :href="to" target="_blank">
     <slot />
@@ -30,7 +51,9 @@ Let's extend RouterLink to handle external links as well and adding a custom `in
     </a>
   </router-link>
 </template>
+```
 
+```vue [Options API]
 <script>
 import { RouterLink } from 'vue-router'
 
@@ -51,8 +74,31 @@ export default {
   },
 }
 </script>
+
+<template>
+  <a v-if="isExternalLink" v-bind="$attrs" :href="to" target="_blank">
+    <slot />
+  </a>
+  <router-link
+    v-else
+    v-bind="$props"
+    custom
+    v-slot="{ isActive, href, navigate }"
+  >
+    <a
+      v-bind="$attrs"
+      :href="href"
+      @click="navigate"
+      :class="isActive ? activeClass : inactiveClass"
+    >
+      <slot />
+    </a>
+  </router-link>
+</template>
 ```
 
+:::
+
 If you prefer using a render function or create `computed` properties, you can use the `useLink` from the [Composition API](./composition-api.md):
 
 ```js
index 7c5f045d8f86ea80047e1a1807a5afc832e59bab..321158ff637c323f095c283111421cffaf60c074 100644 (file)
@@ -38,7 +38,7 @@ First, each route object in the `routes` configuration is called a **route recor
 
 For example, with the above route config, the URL `/posts/new` will match both the parent route record (`path: '/posts'`) and the child route record (`path: 'new'`).
 
-All route records matched by a route are exposed on the `$route` object (and also route objects in navigation guards) as the `$route.matched` Array. We could loop through that array to check all `meta` fields, but Vue Router also provides you a `$route.meta` that is a non-recursive merge of **all `meta`** fields from parent to child. Meaning you can simply write
+All route records matched by a route are exposed on the `route` object (and also route objects in navigation guards) as the `route.matched` Array. We could loop through that array to check all `meta` fields, but Vue Router also provides you a `route.meta` that is a non-recursive merge of **all `meta`** fields from parent to child. Meaning you can simply write:
 
 ```js
 router.beforeEach((to, from) => {
index 57884d3c7eeaab19848c99d66fabab552509baee..059328d3026fe55c028047c236e80a18c3876129 100644 (file)
@@ -224,7 +224,7 @@ It is possible to achieve similar behavior to per-route guards by using [route m
 
 Finally, you can directly define route navigation guards inside route components (the ones passed to the router configuration)
 
-### Using the options API
+### Using the Options API
 
 You can add the following options to route components:
 
@@ -232,9 +232,9 @@ You can add the following options to route components:
 - `beforeRouteUpdate`
 - `beforeRouteLeave`
 
-```js
-const UserDetails = {
-  template: `...`,
+```vue
+<script>
+export default {
   beforeRouteEnter(to, from) {
     // called before the route that renders this component is confirmed.
     // does NOT have access to `this` component instance,
@@ -251,6 +251,7 @@ const UserDetails = {
     // As with `beforeRouteUpdate`, it has access to `this` component instance.
   },
 }
+</script>
 ```
 
 The `beforeRouteEnter` guard does **NOT** have access to `this`, because the guard is called before the navigation is confirmed, thus the new entering component has not even been created yet.
@@ -283,9 +284,9 @@ beforeRouteLeave (to, from) {
 }
 ```
 
-### Using the composition API
+### Using the Composition API
 
-If you are writing your component using the [composition API and a `setup` function](https://vuejs.org/api/composition-api-setup.html), you can add update and leave guards through `onBeforeRouteUpdate` and `onBeforeRouteLeave` respectively. Please refer to the [Composition API section](./composition-api.md#navigation-guards) for more details.
+If you are writing your component using the Composition API, you can add update and leave guards through `onBeforeRouteUpdate` and `onBeforeRouteLeave` respectively. Please refer to the [Composition API section](./composition-api.md#navigation-guards) for more details.
 
 ## The Full Navigation Resolution Flow
 
index f74ab649dd13d3d40595ea6b368739365fc02d36..634dd1586c622624e471560e081a2ccbbfa3198a 100644 (file)
@@ -8,9 +8,7 @@
 Very often we will need to map routes with the given pattern to the same component. For example, we may have a `User` component which should be rendered for all users but with different user IDs. In Vue Router we can use a dynamic segment in the path to achieve that, we call that a _param_:
 
 ```js
-const User = {
-  template: '<div>User</div>',
-}
+import User from './User.vue'
 
 // these are passed to `createRouter`
 const routes = [
@@ -21,22 +19,25 @@ const routes = [
 
 Now URLs like `/users/johnny` and `/users/jolyne` will both map to the same route.
 
-A _param_ is denoted by a colon `:`. When a route is matched, the value of its _params_ will be exposed as `this.$route.params` in every component. Therefore, we can render the current user ID by updating `User`'s template to this:
+A _param_ is denoted by a colon `:`. When a route is matched, the value of its _params_ will be exposed as `route.params` in every component. Therefore, we can render the current user ID by updating `User`'s template to this:
 
-```js
-const User = {
-  template: '<div>User {{ $route.params.id }}</div>',
-}
+```vue
+<template>
+  <div>
+    <!-- The current route is accessible as $route in the template -->
+    User {{ $route.params.id }}
+  </div>
+</template>
 ```
 
-You can have multiple _params_ in the same route, and they will map to corresponding fields on `$route.params`. Examples:
+You can have multiple _params_ in the same route, and they will map to corresponding fields on `route.params`. Examples:
 
-| pattern                        | matched path             | \$route.params                           |
-| ------------------------------ | ------------------------ | ---------------------------------------- |
-| /users/:username               | /users/eduardo           | `{ username: 'eduardo' }`                |
+| pattern                        | matched path             | route.params                           |
+| ------------------------------ | ------------------------ | -------------------------------------- |
+| /users/:username               | /users/eduardo           | `{ username: 'eduardo' }`              |
 | /users/:username/posts/:postId | /users/eduardo/posts/123 | `{ username: 'eduardo', postId: '123' }` |
 
-In addition to `$route.params`, the `$route` object also exposes other useful information such as `$route.query` (if there is a query in the URL), `$route.hash`, etc. You can check out the full details in the [API Reference](../../api/interfaces/RouteLocationNormalized.md).
+In addition to `route.params`, the `route` object also exposes other useful information such as `route.query` (if there is a query in the URL), `route.hash`, etc. You can check out the full details in the [API Reference](../../api/interfaces/RouteLocationNormalized.md).
 
 A working demo of this example can be found [here](https://codesandbox.io/s/route-params-vue-router-examples-mlb14?from-embed&initialpath=%2Fusers%2Feduardo%2Fposts%2F1).
 
@@ -57,34 +58,70 @@ A working demo of this example can be found [here](https://codesandbox.io/s/rout
 
 One thing to note when using routes with params is that when the user navigates from `/users/johnny` to `/users/jolyne`, **the same component instance will be reused**. Since both routes render the same component, this is more efficient than destroying the old instance and then creating a new one. **However, this also means that the lifecycle hooks of the component will not be called**.
 
-To react to params changes in the same component, you can simply watch anything on the `$route` object, in this scenario, the `$route.params`:
+To react to params changes in the same component, you can simply watch anything on the `route` object, in this scenario, the `route.params`:
 
-```js
-const User = {
-  template: '...',
+::: code-group
+
+```vue [Composition API]
+<script setup>
+import { watch } from 'vue'
+import { useRoute } from 'vue-router'
+
+const route = useRoute()
+
+watch(() => route.params.id, (newId, oldId) => {
+  // react to route changes...
+})
+</script>
+```
+
+```vue [Options API]
+<script>
+export default {
   created() {
     this.$watch(
-      () => this.$route.params,
-      (toParams, previousParams) => {
+      () => this.$route.params.id,
+      (newId, oldId) => {
         // react to route changes...
       }
     )
   },
 }
+</script>
 ```
 
+:::
+
 Or, use the `beforeRouteUpdate` [navigation guard](../advanced/navigation-guards.md), which also allows to cancel the navigation:
 
-```js
-const User = {
-  template: '...',
+::: code-group
+
+```vue [Composition API]
+<script setup>
+import { onBeforeRouteUpdate } from 'vue-router'
+// ...
+
+onBeforeRouteUpdate(async (to, from) => {
+  // react to route changes...
+  userData.value = await fetchUser(to.params.id)
+})
+</script>
+```
+
+```vue [Options API]
+<script>
+export default {
   async beforeRouteUpdate(to, from) {
     // react to route changes...
     this.userData = await fetchUser(to.params.id)
   },
+  // ...
 }
+</script>
 ```
 
+:::
+
 ## Catch all / 404 Not found Route
 
 <VueSchoolLink
@@ -96,9 +133,9 @@ Regular params will only match characters in between url fragments, separated by
 
 ```js
 const routes = [
-  // will match everything and put it under `$route.params.pathMatch`
+  // will match everything and put it under `route.params.pathMatch`
   { path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
-  // will match anything starting with `/user-` and put it under `$route.params.afterUser`
+  // will match anything starting with `/user-` and put it under `route.params.afterUser`
   { path: '/user-:afterUser(.*)', component: UserGeneric },
 ]
 ```
@@ -106,13 +143,13 @@ const routes = [
 In this specific scenario, we are using a [custom regexp](./route-matching-syntax.md#custom-regexp-in-params) between parentheses and marking the `pathMatch` param as [optionally repeatable](./route-matching-syntax.md#optional-parameters). This allows us to directly navigate to the route if we need to by splitting the `path` into an array:
 
 ```js
-this.$router.push({
+router.push({
   name: 'NotFound',
   // preserve current path and remove the first char to avoid the target URL starting with `//`
-  params: { pathMatch: this.$route.path.substring(1).split('/') },
+  params: { pathMatch: route.path.substring(1).split('/') },
   // preserve existing query and hash if any
-  query: this.$route.query,
-  hash: this.$route.hash,
+  query: route.query,
+  hash: route.hash,
 })
 ```
 
index 917b73bc53db1f8c2b2710c49e242c02c5df9533..80ea993b3706da27f44d86914a1fb4bf608f872a 100644 (file)
@@ -8,9 +8,9 @@
 Sometimes you need to display multiple views at the same time instead of nesting them, e.g. creating a layout with a `sidebar` view and a `main` view. This is where named views come in handy. Instead of having one single outlet in your view, you can have multiple and give each of them a name. A `router-view` without a name will be given `default` as its name.
 
 ```vue-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>
+<router-view class="view left-sidebar" name="LeftSidebar" />
+<router-view class="view main-content" />
+<router-view class="view right-sidebar" name="RightSidebar" />
 ```
 
 A view is rendered by using a component, therefore multiple views require
index fd3764b1cf9458c6f95b934fda2c7db30da95015..ba2f490efcf1508ee436f52befada30c234c2749 100644 (file)
@@ -22,16 +22,24 @@ With Vue Router, you can express this relationship using nested route configurat
 
 Given the app we created in the last chapter:
 
-```html
-<div id="app">
-  <router-view></router-view>
-</div>
+```vue
+<!-- App.vue -->
+<template>
+  <router-view />
+</template>
+```
+
+```vue
+<!-- User.vue -->
+<template>
+  <div>
+    User {{ $route.params.id }}
+  </div>
+</template>
 ```
 
 ```js
-const User = {
-  template: '<div>User {{ $route.params.id }}</div>',
-}
+import User from './User.vue'
 
 // these are passed to `createRouter`
 const routes = [{ path: '/user/:id', component: User }]
@@ -39,15 +47,14 @@ const routes = [{ path: '/user/:id', component: User }]
 
 The `<router-view>` here is a top-level `router-view`. It renders the component matched by a top level route. Similarly, a rendered component can also contain its own, nested `<router-view>`. For example, if we add one inside the `User` component's template:
 
-```js
-const User = {
-  template: `
-    <div class="user">
-      <h2>User {{ $route.params.id }}</h2>
-      <router-view></router-view>
-    </div>
-  `,
-}
+```vue
+<!-- User.vue -->
+<template>
+  <div class="user">
+    <h2>User {{ $route.params.id }}</h2>
+    <router-view />
+  </div>
+</template>
 ```
 
 To render components into this nested `router-view`, we need to use the `children` option in any of the routes:
index 65bfb85bd76144e940535e93e961bbdf07bfca8d..e5d9f675864e8b91d9508389dc4ba8db731845e8 100644 (file)
@@ -5,26 +5,74 @@
   title="Learn how to pass props to route components"
 />
 
-Using `$route` in your component creates a tight coupling with the route which limits the flexibility of the component as it can only be used on certain URLs. While this is not necessarily a bad thing, we can decouple this behavior with a `props` option:
+Using `$route` or `useRoute()` in your component creates a tight coupling with the route which limits the flexibility of the component as it can only be used on certain URLs. While this is not necessarily a bad thing, we can decouple this behavior with a `props` option.
 
-We can replace
+Let's return to our earlier example:
+
+```vue
+<!-- User.vue -->
+<template>
+  <div>
+    User {{ $route.params.id }}
+  </div>
+</template>
+```
+
+with:
 
 ```js
-const User = {
-  template: '<div>User {{ $route.params.id }}</div>'
+import User from './User.vue'
+
+// these are passed to `createRouter`
+const routes = [
+  { path: '/users/:id', component: User },
+]
+```
+
+We can remove the direct dependency on `$route` in `User.vue` by declaring a prop instead:
+
+::: code-group
+
+```vue [Composition API]
+<!-- User.vue -->
+<script setup>
+defineProps({
+  id: String
+})
+</script>
+
+<template>
+  <div>
+    User {{ id }}
+  </div>
+</template>
+```
+
+```vue [Options API]
+<!-- User.vue -->
+<script>
+export default {
+  props: {
+    id: String
+  }
 }
-const routes = [{ path: '/user/:id', component: User }]
+</script>
+
+<template>
+  <div>
+    User {{ id }}
+  </div>
+</template>
 ```
 
-with
+:::
+
+We can then configure the route to pass the `id` param as a prop by setting `props: true`:
 
 ```js
-const User = {
-  // make sure to add a prop named exactly like the route param
-  props: ['id'],
-  template: '<div>User {{ id }}</div>'
-}
-const routes = [{ path: '/user/:id', component: User, props: true }]
+const routes = [
+  { path: '/user/:id', component: User, props: true }
+]
 ```
 
 This allows you to use the component anywhere, which makes the component easier to reuse and test.
index 3ba003da29b93d0f700d4421d6e47b556aaeba26..3b31648bc5a9106d584ab95d17ce0abde830d82f 100644 (file)
 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">Watch a Free Vue Router Video Course</VueSchoolLink>
 
-Creating a Single-page Application with Vue + Vue Router feels natural: with Vue.js, we are already composing our application with components. When adding Vue Router to the mix, all we need to do is map our components to the routes and let Vue Router know where to render them. Here's a basic example:
+Vue Router is the official client-side routing solution for Vue.
 
-## HTML
+Client-side routing is used by single-page applications (SPAs) to tie the browser URL to the content seen by the user. As users navigate around the application, the URL updates accordingly, but the page doesn't need to be reloaded from the server.
 
-```html
-<script src="https://unpkg.com/vue@3"></script>
-<script src="https://unpkg.com/vue-router@4"></script>
+Vue Router is built on Vue's component system. You configure **routes** to tell Vue Router which components to show for each URL path.
 
-<div id="app">
+::: tip Prerequisites
+This guide will assume that you are already familiar with Vue itself. You don't need to be a Vue expert, but you may occasionally need to refer back to [the core Vue documentation](https://vuejs.org/) for more information about certain features.
+:::
+
+## An example
+
+To introduce some of the main ideas, we're going to consider this example:
+
+- [Vue Playground example](https://play.vuejs.org/#eNqFVVtv2zYU/itn6gArmC05btEHTXXTFcWyYZeiLfYy7UGWji02EsmRlOPA8H/fIambnaRD4Fg61++c7yN9DJqc8eirDpKANVIoA0coFOYG30kJJ9gq0cBs3+Is412AEq1B1Xmi2L+ObpvX+3IpI5+b8aFqSJ+rjANErcbQp/v3RrTchLMXlDa7CuZBl07YUoONrCl/bQPT6np9i3UtbLPv0phenVm6L3rQRgm+W79vlULeIQaZmypJ484HxyN87xzRtq3rj+SE08mViX2dlOf7vuAnh/I3xu/AiDdZEGfB+mdBz3ArGkzj0f9sRr4hy5D2zr49ykvjvmdqeTmv9RfDe4i7uM6dxsNiaF9+l0+y+Ts2Qj3cMm3oa94Zfd0py4uBzYFPO6Br3ZPaGzpme9rtQGdxg2WUgOC6Y0PDG/jbjnL0vMAsnhEsQcU4UZaMbU/z8zC3x/PYsbcN/ueilaJW03nDoy1Y+VUkT+0nvHI9PVB6PJE8M44HN2iJ27yt+9q09ek+rFR1oZg0RM5FgmvboKlEqRP/BrATX4SDH171JgBD4CIvThXJVldhP7Y7J9DtxP4nxZKk+470cnFQVuseHh2TlTduWmMEh5uiZsUdSXPAcKlOH/hIZmfEjhODRtPaozNKjyiiGcqn75Ej0Pl3lMyHp2fFeMHnEB/SRia+ict6ep/GXBWV1UGHyGtgh5O1K0KvuC8T/duieoi6tLdvYUYg+rXTmKH3jLmeKoW0owLDI7h8IrnvfAKrIargxfQ/lA0LHjmr8w3W3X3w2dVMIGWchoH9ohEl1pFRrCE2fccsgCY/1Mh3piLjaknc+pujr3TOqedk0eSSrg/BiVU3WtY5dBYMks2CkRtrzoLKGKmTOG65vNtFtON4jLh5Fb2MlnFJJ2tijVA3i40S99rdV1ngNmtr31BQXOLeCFHrRS7Zcy0eBd68jl5H13HNNjFVjxkv8eBq94unMY0mQWzZ7mJIKwtWo/pTGkaCORs2p9+Z+1+dzagWB6BFhcXdE/av+uAhf1RI0+1xMpzJFWnOuz98/gMP9Dw4icW2puhvOD+hFnVrMfqwn1peEuxJnEP7i+OM8d0X/eFgkOt+KAt0FLIj8v03Rh/hvoxeTbaozUONOiq0/aGhX6w5aY1xn7cRqkSVwEoegMCyEl4sl8sf3d1H5RhfbATdKk0C10t5cHaZlyWBHSzUJeNUFtaQww/08Tenz65xSzf+NLJaTTuP5UcARVFMACSwpL9VVyE4/QesCg/V)
+
+Let's start by looking at the root component, `App.vue`.
+
+### App.vue
+
+```vue
+<template>
   <h1>Hello App!</h1>
   <p>
-    <!-- use the router-link component for navigation. -->
-    <!-- specify the link by passing the `to` prop. -->
-    <!-- `<router-link>` will render an `<a>` tag with -->
-    <!-- the correct `href` attribute -->
-    <router-link to="/">Go to Home</router-link>
-    <router-link to="/about">Go to About</router-link>
+    <strong>Current route path:</strong> {{ $route.fullPath }}
   </p>
-  <!-- route outlet -->
-  <!-- component matched by the route will render here -->
-  <router-view></router-view>
-</div>
+  <nav>
+    <RouterLink to="/">Go to Home</RouterLink>
+    <RouterLink to="/about">Go to About</RouterLink>
+  </nav>
+  <main>
+    <RouterView />
+  </main>
+</template>
 ```
 
-### `router-link`
+This template is using two components provided by Vue Router, `RouterLink` and `RouterView`.
 
-Note how instead of using regular `a` tags, we use a custom component `router-link` to create links. This allows Vue Router to change the URL without reloading the page, handle URL generation as well as its encoding. We will see later how to benefit from these features.
+Instead of using regular `<a>` tags, we use the custom component `RouterLink` to create links. This allows Vue Router to change the URL without reloading the page, handle URL generation, encoding, and various other features. We'll go into more detail about `RouterLink` in later sections of the guide.
 
-### `router-view`
+The `RouterView` component tells Vue Router where to render the current **route component**. That's the component that corresponds to the current URL path. It doesn't have to be in `App.vue`, you can put it anywhere to adapt it to your layout, but it does need to be included somewhere, otherwise Vue Router won't render anything.
 
-`router-view` will display the component that corresponds to the URL. You can put it anywhere to adapt it to your layout.
+The example above also uses <code v-pre>{{ $route.fullPath }}</code>. You can use `$route` in your component templates to access an object that represents the current route.
 
 <VueMasteryLogoLink></VueMasteryLogoLink>
 
-## JavaScript
+### Creating the router instance
+
+The router instance is created by calling the function `createRouter()`:
 
 ```js
-// 1. Define route components.
-// These can be imported from other files
-const Home = { template: '<div>Home</div>' }
-const About = { template: '<div>About</div>' }
-
-// 2. Define some routes
-// Each route should map to a component.
-// We'll talk about nested routes later.
+import { createMemoryHistory, createRouter } from 'vue-router'
+
+import HomeView from './HomeView.vue'
+import AboutView from './AboutView.vue'
+
 const routes = [
-  { path: '/', component: Home },
-  { path: '/about', component: About },
+  { path: '/', component: HomeView },
+  { path: '/about', component: AboutView },
 ]
 
-// 3. Create the router instance and pass the `routes` option
-// You can pass in additional options here, but let's
-// keep it simple for now.
-const router = VueRouter.createRouter({
-  // 4. Provide the history implementation to use. We
-  // are using the hash history for simplicity here.
-  history: VueRouter.createWebHashHistory(),
-  routes, // short for `routes: routes`
+const router = createRouter({
+  history: createMemoryHistory(),
+  routes,
 })
+```
 
-// 5. Create and mount the root instance.
-const app = Vue.createApp({})
-// Make sure to _use_ the router instance to make the
-// whole app router-aware.
-app.use(router)
+The `routes` option defines the routes themselves, mapping URL paths to components. The component specified by the `component` option is the one that will be rendered by the `<RouterView>` in our earlier `App.vue`. These route components are sometimes referred to as _views_, though they are just normal Vue components.
 
-app.mount('#app')
+Routes support various other options that we'll see later in the guide, but for now we only need `path` and `component`.
+
+The `history` option controls how routes are mapped onto URLs and vice versa. For the Playground example we're using `createMemoryHistory()`, which ignores the browser URL entirely and uses its own internal URL instead. That works well for the Playground, but it's unlikely to be what you'd want in a real application. Typically, you'd want to use `createWebHistory()` instead, or perhaps `createWebHashHistory()`. We'll cover that topic in more detail in the guide to [History modes](./essentials/history-mode).
 
-// Now the app has started!
+### Registering the router plugin
+
+Once we've created our router instance, we need to register it as a plugin by calling `use()` on our application:
+
+```js
+createApp(App)
+  .use(router)
+  .mount('#app')
 ```
 
-By calling `app.use(router)`, we are triggering the initial navigation and giving access to `this.$router` as well as the current route as `this.$route` inside of any component:
+Or, equivalently:
+
+```js
+const app = createApp(App)
+app.use(router)
+app.mount('#app')
+```
+
+Like with most Vue plugins, the call to `use()` needs to happen before the call to `mount()`.
+
+If you're curious about what this plugin does, some of its responsibilities include:
+
+1. [Globally registering](https://vuejs.org/guide/components/registration.html#global-registration) the `RouterView` and `RouterLink` components.
+2. Adding the global `$router` and `$route` properties.
+3. Enabling the `useRouter()` and `useRoute()` composables.
+4. Triggering the router to resolve the initial route.
+
+### Accessing the router and current route
+
+You'll likely want to access the router from elsewhere in your application.
+
+If you're exporting the router instance from an ES module, you could import the router instance directly where you need it. In some cases this is the best approach, but we have other options if we're inside a component.
+
+In component templates, the router instance is exposed as `$router`. This is similar to the `$route` property we saw earlier, but note the extra `r` on the end.
+
+If we're using the Options API, we can access these same two properties as `this.$router` and `this.$route` in our JavaScript code. The `HomeView.vue` component in the Playground example accesses the router that way:
 
 ```js
-// Home.vue
 export default {
-  computed: {
-    username() {
-      // We will see what `params` is shortly
-      return this.$route.params.username
-    },
-  },
   methods: {
-    goToDashboard() {
-      if (isAuthenticated) {
-        this.$router.push('/dashboard')
-      } else {
-        this.$router.push('/login')
-      }
+    goToAbout() {
+      this.$router.push('/about')
     },
   },
 }
 ```
 
-To access the router or the route inside the `setup` function, call the `useRouter` or `useRoute` functions. We will learn more about this in [the Composition API](./advanced/composition-api.md#Accessing-the-Router-and-current-Route-inside-setup)
+This method is calling `push()`, which is used for [programmatic navigation](./essentials/navigation). We'll learn more about that later.
+
+With the Composition API, we don't have access to the component instance via `this`, so Vue Router includes some composables that we can use instead. `AboutView.vue` in the Playground example is using that approach:
+
+```vue
+<script setup>
+import { computed } from 'vue'
+import { useRoute, useRouter } from 'vue-router'
+
+const router = useRouter()
+const route = useRoute()
+
+const search = computed({
+  get() {
+    return route.query.search ?? ''
+  },
+  set(search) {
+    router.replace({ query: { search } })
+  }
+})
+</script>
+```
+
+It's not necessary to understand all of that code right now. The key thing to notice is that the composables `useRouter()` and `useRoute()` are used to access the router instance and current route respectively.
+
+### Next steps
+
+If you'd like to see a complete example using Vite, you can use the [create-vue](https://github.com/vuejs/create-vue) scaffolding tool, which has the option to include Vue Router in its example project:
+
+::: code-group
+
+```bash [npm]
+npm create vue@latest
+```
+
+```bash [yarn]
+yarn create vue
+```
+
+```bash [pnpm]
+pnpm create vue
+```
+
+:::
+
+The example project created by create-vue uses similar features to the ones we've seen here. You may find that a useful starting point for exploring the features introduced in the next few pages of this guide.
+
+## Conventions in this guide
+
+### Single-File Components
+
+Vue Router is most commonly used in applications built using a bundler (e.g. Vite) and [SFCs](https://vuejs.org/guide/introduction.html#single-file-components) (i.e. `.vue` files). Most of the examples in this guide will be written in that style, but Vue Router itself doesn't require you to use build tools or SFCs.
+
+For example, if you're using the _global builds_ of [Vue](https://vuejs.org/guide/quick-start.html#using-vue-from-cdn) and [Vue Router](../installation#Direct-Download-CDN), the libraries are exposed via global objects, rather than imports:
+
+```js
+const { createApp } = Vue
+const { createRouter, createWebHistory } = VueRouter
+```
+
+### Component API style
+
+Vue Router can be used with both the Composition API and the Options API. Where relevant, the examples in this guide will show components written in both styles. Composition API examples will typically use `<script setup>`, rather than an explicit `setup` function.
+
+If you need a refresher about the two styles, see [Vue - API Styles](https://vuejs.org/guide/introduction.html#api-styles).
+
+### `router` and `route`
+
+Throughout the guide, we will often refer to the router instance as `router`. This is the object returned by `createRouter()`. How you access that object in your application will depend on the context. For example, in a component using the Composition API, it can be accessed by calling `useRouter()`. With the Options API, it can be accessed using `this.$router`.
+
+Similarly, the current route will be referred to as `route`. It can be accessed in components using `useRoute()` or `this.$route`, depending on which API the component is using.
+
+### `RouterView` and `RouterLink`
+
+The components `RouterView` and `RouterLink` are both [registered globally](https://vuejs.org/guide/components/registration.html#global-registration), so they don't need to be imported before using them in component templates. However, if you prefer, you can import them locally, e.g. `import { RouterLink } from 'vue-router'`.
+
+In templates, component names can be written in either PascalCase or kebab-case. Vue's template compiler supports either format, so `<RouterView>` and `<router-view>` are usually equivalent. You should follow whatever convention is used within your project.
 
-Throughout the docs, we will often use the `router` instance. Keep in mind that `this.$router` is exactly the same as directly using the `router` instance created through `createRouter`. The reason we use `this.$router` is because we don't want to import the router in every single component that needs to manipulate routing.
+If you're using in-DOM templates then [the usual caveats](https://vuejs.org/guide/essentials/component-basics.html#in-dom-template-parsing-caveats) apply: component names must be written in kebab-case and self-closing tags are not supported. So rather than writing `<RouterView />`, you would need to use `<router-view></router-view>` instead.
\ No newline at end of file
index 6677e33b8b8148d393c3691611cb4f50b7312575..1dbec1bb463c839f1aeaca1be0fcb1f3f00096a0 100644 (file)
@@ -2,18 +2,6 @@
 
 <VueMasteryLogoLink></VueMasteryLogoLink>
 
-## Direct Download / CDN
-
-[https://unpkg.com/vue-router@4](https://unpkg.com/vue-router@4)
-
-<!--email_off-->
-
-[Unpkg.com](https://unpkg.com) provides npm-based CDN links. The above link will always point to the latest release on npm. You can also use a specific version/tag via URLs like `https://unpkg.com/vue-router@4.0.15/dist/vue-router.global.js`.
-
-<!--/email_off-->
-
-This will expose Vue Router via a global `VueRouter` object, e.g. `VueRouter.createRouter(...)`.
-
 ## Package managers
 
 If you have an existing project that uses a JavaScript package manager, you can install Vue Router from the npm registry:
@@ -54,4 +42,16 @@ pnpm create vue
 
 You'll be prompted with some questions about the kind of project you want to create. If you choose to install Vue Router, the example application will also demonstrate some of Vue Router's core features.
 
-Projects using package managers will typically use ES modules to access Vue Router, e.g. `import { createRouter } from 'vue-router'`.
\ No newline at end of file
+Projects using package managers will typically use ES modules to access Vue Router, e.g. `import { createRouter } from 'vue-router'`.
+
+## Direct Download / CDN
+
+[https://unpkg.com/vue-router@4](https://unpkg.com/vue-router@4)
+
+<!--email_off-->
+
+[Unpkg.com](https://unpkg.com) provides npm-based CDN links. The above link will always point to the latest release on npm. You can also use a specific version/tag via URLs like `https://unpkg.com/vue-router@4.0.15/dist/vue-router.global.js`.
+
+<!--/email_off-->
+
+This will expose Vue Router via a global `VueRouter` object, e.g. `VueRouter.createRouter(...)`.