The Vue.js Composition API is a set of function-based APIs that allows you to make a flexible composition of component logic. This is really useful to write reusable logic and to make code well organized.
This article will introduce how to directly watch the route object in a Vue project.
Here’s the final codebase in GitHub: manakuro/reactive-vue-route-in-composition-api-example.
To quickly start off, we will create a Nuxt project:
npx create-nuxt-app my-app
After installation, you can start the dev server by running the command:
yarn dev
Install the Composition API:
yarn add @vue/composition-api
Create a plugins/composition-api.js
:
import Vue from 'vue'
import VueCompositionApi from '@vue/composition-api'
Vue.use(VueCompositionApi)
Add it to plugins in nuxt.config.js
:
plugins: ['~/plugins/composition-api'],
Now that we can use the Composition API in SFC, let’s make use of the feature.
Let’s suppose that we have a button that changes query params in URL, like this:
<template>
<div class="container">
<div>
<logo />
<h2 class="subtitle">Reactive Router query: {{ myQuery }}</h2>
<div class="links">
<a
class="button--grey"
@click="handleClick"
>
Change Router Query
</a>
</div>
</div>
</div>
</template>
<script lang="ts">
import { computed, defineComponent } from '@vue/composition-api'
import Logo from '~/components/Logo.vue'
export default defineComponent({
components: {
Logo
},
setup(_, ctx) {
const route = ctx.root.$route
const myQuery = computed(() => route.query.myQuery)
const handleClick = (e: Event) => {
e.preventDefault()
ctx.root.$router.push({ query: { myQuery: 'handle my query!' } })
}
return {
myQuery,
handleClick
}
}
})
</script>
index.vue
When you want to use the route
in the set-up function, you can access it through the ctx
, which includes the Vue instance object.
Let’s see how it works:
I expected the text to be changed to React Router query: handle my query!
after clicking the button. But it didn’t change because the route object is just a plain object, not a reactive one.
To make it work, we need to make the route reactive by passing a reference.
Change the code to this:
<script lang="ts">
import {
computed,
defineComponent,
reactive,
watch
} from '@vue/composition-api'
import Logo from '~/components/Logo.vue'
export default defineComponent({
components: {
Logo
},
setup(_, ctx) {
const route = ctx.root.$route
const state = reactive({ route })
watch(
() => ctx.root.$route,
(r) => {
state.route = r as any
}
)
const myQuery = computed(() => state.route.query.myQuery)
const handleClick = (e: Event) => {
e.preventDefault()
ctx.root.$router.push({ query: { myQuery: 'handle my query!' } })
}
return {
myQuery,
handleClick
}
}
})
</script>
index.vue
We’ve added a reactive state
, including the route object. The reactive
function equals Vue.observable
, which makes an object reactive. Internally, Vue uses this on the object returned by the data
function.
We’ve made $route
watchable by using the watch
function because we need to update the state.route
object with a new one after $router
has changed something.
Let’s see how it works:
Good. It works well.
Now that we’ve made it, we want to make the route more reusable across the project.
To do that, we will extract the code and create useRouter
as a Hook function.
Let’s create hooks/useRouter.ts
:
import { reactive, toRefs, watch } from '@vue/composition-api'
import { Route } from 'vue-router'
import { getRuntimeVM } from '~/utils/runtime'
type State = {
route: Route
}
const useRouter = () => {
const vm = getRuntimeVM()
const state = reactive<State>({
route: vm.$route
})
watch(
() => vm.$route,
(r) => {
state.route = r as any
}
)
return { ...toRefs(state), router: vm.$router }
}
useRouter.ts
toRefs
is useful when returning a reactive object from a composition function to destructure the returned object without losing reactivity.
Then we edit the Vue file:
<script lang="ts">
import { computed, defineComponent } from '@vue/composition-api'
import Logo from '~/components/Logo.vue'
import useRouter from '~/hooks/useRouter'
export default defineComponent({
components: {
Logo
},
setup() {
const { route, router } = useRouter()
const myQuery = computed(() => route.value.query.myQuery)
const handleClick = (e: Event) => {
e.preventDefault()
router.push({ query: { myQuery: 'handle my query!' } })
}
return {
myQuery,
handleClick
}
}
})
</script>
index.vue
Good! This is very declarative and makes it a more reusable function.
We’ve covered how to watch the route object with the Composition API. The key idea of the Composition API is to make code organized, readable, and reusable. If you find yourself writing a bunch of code in the set-up function, just try to separate it by concerns and create a useXXX
function to reuse it. You can see some examples of code on GitHub.
☞ Vue js Tutorial Zero to Hero || Brief Overview about Vue.js || Learn VueJS 2023 || JS Framework
☞ Learn Vue.js from scratch 2018
☞ Is Vue.js 3.0 Breaking Vue? Vue 3.0 Preview!
☞ Vue.js Tutorial: Zero to Sixty