From 214eb308fce03966e666c2172b90f18a01b7f8dd Mon Sep 17 00:00:00 2001 From: Eduardo San Martin Morote Date: Sat, 5 Sep 2020 13:51:58 +0200 Subject: [PATCH] docs: add composition api page --- README.md | 2 + docs/.vitepress/config.js | 12 ++-- docs/guide/advanced/composition-api.md | 79 ++++++++++++++++++++++++ docs/guide/advanced/navigation-guards.md | 19 +----- docs/guide/index.md | 32 ++++++---- docs/guide/migration/index.md | 27 +++++--- src/RouterLink.ts | 10 +-- 7 files changed, 135 insertions(+), 46 deletions(-) create mode 100644 docs/guide/advanced/composition-api.md diff --git a/README.md b/README.md index 619ba2cd..c466565d 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,8 @@ ## Know issues + + ### Breaking changes compared to vue-router@3.x - The `mode: 'history'` option has been replaced with a more flexible one named `history`: diff --git a/docs/.vitepress/config.js b/docs/.vitepress/config.js index 344414fa..b8dcae5c 100644 --- a/docs/.vitepress/config.js +++ b/docs/.vitepress/config.js @@ -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 index 00000000..a686f58c --- /dev/null +++ b/docs/guide/advanced/composition-api.md @@ -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) + } + }) + }, +} +``` + + diff --git a/docs/guide/advanced/navigation-guards.md b/docs/guide/advanced/navigation-guards.md index 863db441..b4fb075c 100644 --- a/docs/guide/advanced/navigation-guards.md +++ b/docs/guide/advanced/navigation-guards.md @@ -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 diff --git a/docs/guide/index.md b/docs/guide/index.md index 3fe1f923..5c8bc817 100644 --- a/docs/guide/index.md +++ b/docs/guide/index.md @@ -50,7 +50,7 @@ const About = { template: '
About
' } // 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. diff --git a/docs/guide/migration/index.md b/docs/guide/migration/index.md index e84111b0..934fda3b 100644 --- a/docs/guide/migration/index.md +++ b/docs/guide/migration/index.md @@ -9,12 +9,10 @@ Some of new features to keep an eye on in Vue Router 4 include: - 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 ``. You can manually concat replace to relative child with -to relative child + + to relative child + +``` + +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 `` @@ -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` | | -------------- | ----------------------- | diff --git a/src/RouterLink.ts b/src/RouterLink.ts index 68b77786..06c27bf4 100644 --- a/src/RouterLink.ts +++ b/src/RouterLink.ts @@ -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 -- 2.47.2