]> git.ipfire.org Git - thirdparty/vuejs/router.git/commitdiff
docs: add composition api page
authorEduardo San Martin Morote <posva13@gmail.com>
Sat, 5 Sep 2020 11:51:58 +0000 (13:51 +0200)
committerEduardo San Martin Morote <posva13@gmail.com>
Sat, 5 Sep 2020 11:51:58 +0000 (13:51 +0200)
README.md
docs/.vitepress/config.js
docs/guide/advanced/composition-api.md [new file with mode: 0644]
docs/guide/advanced/navigation-guards.md
docs/guide/index.md
docs/guide/migration/index.md
src/RouterLink.ts

index 619ba2cd9ad0e370b3d68e5974d2c856747cd9eb..c466565dfbb972457b57f4c7166e0b89129d9f20 100644 (file)
--- a/README.md
+++ b/README.md
@@ -9,6 +9,8 @@
 
 ## Know issues
 
+<!-- TODO: put a link to the documentation instead -->
+
 ### Breaking changes compared to vue-router@3.x
 
 - The `mode: 'history'` option has been replaced with a more flexible one named `history`:
index 344414fad0037b53cd691954c679a4a64e08f3e1..b8dcae5cc26fd1c47c0e07aa50732905216bd96c 100644 (file)
@@ -98,14 +98,18 @@ const config = {
                 text: 'Navigation guards',
               },
               { link: '/guide/advanced/meta', text: 'Route Meta Fields' },
-              {
-                link: '/guide/advanced/transitions',
-                text: 'Transitions',
-              },
               {
                 link: '/guide/advanced/data-fetching',
                 text: 'Data Fetching',
               },
+              {
+                link: '/guide/advanced/composition-api',
+                text: 'Composition API',
+              },
+              {
+                link: '/guide/advanced/transitions',
+                text: 'Transitions',
+              },
               {
                 link: '/guide/advanced/scroll-behavior',
                 text: 'Scroll Behavior',
diff --git a/docs/guide/advanced/composition-api.md b/docs/guide/advanced/composition-api.md
new file mode 100644 (file)
index 0000000..a686f58
--- /dev/null
@@ -0,0 +1,79 @@
+# Vue Router and the Composition API
+
+The introduction of `setup` and Vue's [Composition API](https://v3.vuejs.org/guide/composition-api-introduction.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.
+
+## 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` function:
+
+```js
+export default {
+  setup() {
+    const router = useRouter()
+    const route = useRoute()
+
+    function pushWithQuery(query) {
+      router.push({
+        name: 'search',
+        query: {
+          ...this.route.query,
+        },
+      })
+    }
+  },
+}
+```
+
+The `route` object is a reactive object, so any of its properties can be watched and you should **avoid watching the whole `route`** object:
+
+```js
+export default {
+  setup() {
+    const route = useRoute()
+    const userData = ref()
+
+    // fetch the user information when params change
+    watch(
+      () => route.params,
+      async params => {
+        userData.value = await fetchUser(newParams.id)
+      }
+    )
+  },
+}
+```
+
+Note we still have access to `$router` and `$route` in templates, so there is no need to return `router` or `route` inside of `setup`.
+
+## 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:
+
+```js
+import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
+
+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)
+      }
+    })
+  },
+}
+```
+
+<!-- TODO: useLink -->
index 863db44169918bc80316f2dab5b926c46118dd7a..b4fb075c61b7ba1593e27173a36e4f5c58856fad 100644 (file)
@@ -225,24 +225,7 @@ beforeRouteLeave (to, from) {
 
 ### Using the composition API
 
-If you are writing your component using the [composition API and a `setup` function](https://v3.vuejs.org/guide/composition-api-setup.html#setup), you can add update and leave guards:
-
-```js
-import { onBeforeRouteUpdate, onBeforeRouteLeave } from 'vue-router'
-
-const UserDetails = {
-  template: `...`,
-  setup() {
-    onBeforeRouteUpdate((to, from) => {
-      // same as the beforeRouteUpdate option but with no access to `this`
-    })
-
-    onBeforeRouteLeave((to, from) => {
-      // same as the beforeRouteLeave option but with no access to `this`
-    })
-  },
-}
-```
+If you are writing your component using the [composition API and a `setup` function](https://v3.vuejs.org/guide/composition-api-setup.html#setup), 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 3fe1f923e63179e32e6b52701b1de38f754c1c4d..5c8bc8173dc395d57a758c759cf6f5d9da6117d2 100644 (file)
@@ -50,7 +50,7 @@ const About = { template: '<div>About</div>' }
 // We'll talk about nested routes later.
 const routes = [
   { path: '/', component: Home },
-  { path: '/about', component: About }
+  { path: '/about', component: About },
 ]
 
 // 3. Create the router instance and pass the `routes` option
@@ -59,7 +59,7 @@ const routes = [
 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`
+  routes, // short for `routes: routes`
 })
 
 // 4. Create and mount the root instance.
@@ -82,13 +82,17 @@ export default {
     username() {
       // We will see what `params` is shortly
       return this.$route.params.username
-    }
+    },
   },
   methods: {
-    goBack() {
-      window.history.length > 1 ? this.$router.go(-1) : this.$router.push('/')
-    }
-  }
+    goToDashboard() {
+      if (isAuthenticated) {
+        this.$router.push('/dashboard')
+      } else {
+        this.$router.push('/login')
+      }
+    },
+  },
 }
 ```
 
@@ -105,13 +109,19 @@ export default {
     const route = useRoute()
 
     const username = computed(() => route.params.username)
-    function goBack() {
-      window.history.length > 1 ? router.go(-1) : router.push('/')
+    function goToDashboard() {
+      if (isAuthenticated) {
+        router.push('/dashboard')
+      } else {
+        router.push('/login')
+      }
     }
 
-    return { username, goBack }
-  }
+    return { username, goToDashboard }
+  },
 }
 ```
 
+We will learn more about this in [the Composition API](/guide/advanced/composition-api.md)
+
 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.
index e84111b083a710f7ae0715cc1b02ae3267035f17..934fda3b242ca62be5a5c09a38256e619fe866bb 100644 (file)
@@ -9,12 +9,10 @@ Some of new features to keep an eye on in Vue Router 4 include:
 <!-- TODO: links -->
 
 - Dynamic Routing
-- Composition API
+- [Composition API](/guide/advanced/composition-api.md)
 - Custom History implementation
 
-## Breaking Changes
-
-### Improvements
+## Breaking Changes: Improvements
 
 The following changes should not be a problem for you but they are technically breaking changes that will show a different behavior and might break parts of your application.
 
@@ -99,7 +97,9 @@ Given any [normalized route location](#TODO):
 
 **Reason**: This allows to easily copy existing properties of a location when calling `router.push()` and `router.resolve()`. Learn more about encoding [in the cookbook](/cookbook/encoding.md).
 
-### Changed API
+## Breaking Changes: API Changes
+
+The following changes will require you to adapt your code
 
 ### New `history` option to replace `mode`
 
@@ -166,7 +166,7 @@ router.resolve({
 }).href // '/not/found'
 ```
 
-**Reason**: Vue Router doesn't use `path-to-regexp` anymore, instead it implements its own parsing system that allows route ranking and enables dynamic routing.
+**Reason**: Vue Router doesn't use `path-to-regexp` anymore, instead it implements its own parsing system that allows route ranking and enables dynamic routing. Since we usually add one single catch-all route per project, there is no big benefit in supporting a special syntax for `*`.
 
 ### Removal of `router.match` and changes to `router.resolve`
 
@@ -194,10 +194,19 @@ The `append` prop has been removed from `<router-link>`. You can manually concat
 replace
 <router-link to="child-route" append>to relative child</router-link>
 with
-<router-link :to="$route.path + '/child-route'">to relative child</router-link>
+<router-link :to="append($route.path, 'child-route')">
+  to relative child
+</router-link>
+```
+
+You must define a global `append` function on your _App_ instance:
+
+```js
+app.config.globalProperties.append = (path, pathToAppend) =>
+  path + (path.endsWith('/') ? '' : '/') + pathToAppend
 ```
 
-**Reason**: `append` wasn't used very often and is easy to replicate in user land.
+**Reason**: `append` wasn't used very often, is easy to replicate in user land.
 
 ### Removal of `event` and `tag` props in `<router-link>`
 
@@ -270,7 +279,7 @@ The `pathToRegexpOptions` and `caseSensitive` properties of route records have b
 
 ### TypeScript
 
-To make typings more consistent and expressive, some types have been renamed.
+To make typings more consistent and expressive, some types have been renamed:
 
 | `vue-router@3` | `vue-router@4`          |
 | -------------- | ----------------------- |
index 68b777862a08b990033ff1381ec6d065449a8b9e..06c27bf4aa11291c2e137dc716df690e457cbb7e 100644 (file)
@@ -18,7 +18,7 @@ import { assign } from './utils'
 
 export interface RouterLinkOptions {
   /**
-   * Location the link should navigate to when clicked on.
+   * Route Location the link should navigate to when clicked on.
    */
   to: RouteLocationRaw
   /**
@@ -30,7 +30,8 @@ export interface RouterLinkOptions {
 
 export interface RouterLinkProps extends RouterLinkOptions {
   /**
-   * Whether RouterLink should not wrap its content in an `a` tag.
+   * Whether RouterLink should not wrap its content in an `a` tag. Useful when
+   * using `v-slot` to create a custom RouterLink
    */
   custom?: boolean
   /**
@@ -81,8 +82,9 @@ export function useLink(props: UseLinkOptions) {
     return (
       // we are dealing with nested routes
       length > 1 &&
-        // if the have the same path, this link is referring to the empty child
-        // are we currently are on a different child of the same parent
+        // if the parent and matched route have the same path, this link is
+        // referring to the empty child. Or we currently are on a different
+        // child of the same parent
         getOriginalPath(routeMatched) === parentRecordPath &&
         // avoid comparing the child with its parent
         currentMatched[currentMatched.length - 1].path !== parentRecordPath