Lifecycle methods also have changed
npm i @vue/composition-api
import VueCompositionAPI from '@vue/composition-api'; Vue.use(VueCompositionAPI); new Vue({...}) // regular
<script lang="ts"> import { defineComponent, ref } from '@vue/composition-api'; export default defineComponent({ components: { ... }, setup() { return { }; }, }); </script>
Instead import { ref } from "vue"; import { ref } from "@vue/composition-api";
It is reactive and can be watched
import { watch } from "vue"; export default { props: { name: String }, setup(props) { watch(() => { console.log(props.name); <-- console whenever it changes }); } };
Context has access to properties previously accessed by "this"
setup(props, context) { context.attrs; <--- i.e. class context.slots; context.parent; context.root; context.emit; <--- context.emit('my-event', value) }
<template> <div>Capacity: {{ capacity }}</div> </template> <script> import { ref } from "vue"; export default { setup() { const capacity = ref(3); <-- is creating a Reactive Reference. return { capacity }; <-- This returned object is how we expose which data we need access to in the renderContext. } }; </script>
reactive
is for objects only
<template> <section> <h2>{{ user.name }}</h2> </section> </template> <script> import { defineComponent, reactive } from '@vue/composition-api'; export default defineComponent({ setup() { const user = reactive({ name: 'Dana', age: 18 }); return { user }; } }); </script>
reactive
and toRefs
<template> <section> <h2>Name: {{ name }}</h2> --- only name, not user.name <h3>Age: {{ age }}</h3> </section> </template> <script> import { defineComponent, reactive, toRefs } from '@vue/composition-api'; export default defineComponent({ setup() { const user = reactive({ name: 'Dana', age: 18 }); return { ...toRefs(user), xyz }; OR return toRefs(user); } }); </script>
user.value.name
<template> <h2>{{ user.name }}</h2> </template> <script> import { defineComponent, ref } from '@vue/composition-api'; export default defineComponent({ setup() { const user = ref({ name: 'Dana', age: 18 }); return { user }; --- raw ref object, not user.value.name (because user.value.name is not reactive) } }); </script>
<template> <section> <input type="text" placeholder="First Name" @input="setFirstName" /> <input type="text" placeholder="Last Name" @input="setLastName" /> <div>First and last name: {{ firstName }} {{ lastName }}</div> </section> </template> <script> import { ref, computed } from "@vue/composition-api"; export default defineComponent({ setup() { const firstName = ref(""); const lastName = ref(""); function setFirstName(event) { firstName.value = event.target.value; } function setLastName(event) { lastName.value = event.target.value; } const firstAndLast = computed(() => { return firstName.value + ' ' + lastName.value; }) return { firstName, lastName, setFirstName, setLastName }; }, }); </script>
<template> <div> <p>Capacity: {{ capacity }}</p> <button @click="increaseCapacity()">Increase Capacity</button> <---- </div> </template>
<script> import { ref } from "vue"; export default { setup() { const capacity = ref(3); function increaseCapacity() { // <--- Our new function capacity.value++; } return { capacity, increaseCapacity }; } }; </script>
<template> <div> <p>Spaces Left: {{ spacesLeft }} out of {{ capacity }}</p> <h2>Attending</h2> <ul> <li v-for="(name, index) in attending" :key="index"> {{ name }} </li> </ul> <button @click="increaseCapacity()">Increase Capacity</button> </div> </template> <script> import { ref, computed } from "vue"; <--- export default { setup() { const capacity = ref(4); const attending = ref(["Tim", "Bob", "Joe"]); const spacesLeft = computed(() => { // <------- return capacity.value - attending.value.length; }); function increaseCapacity() { capacity.value++; } return { capacity, attending, spacesLeft, increaseCapacity }; } }; </script>
import { reactive, computed, toRefs } from "vue"; <--- export default { setup() { const event = reactive({ <--- returns a reactive object capacity: 4, attending: ["Tim", "Bob", "Joe"], spacesLeft: computed(() => { return event.capacity - event.attending.length; <--- there is no .value }) }); function increaseCapacity() { event.capacity++; } return { ...toRefs(event), increaseCapacity }; // return toRefs(event); <---- toRefs used for destructuring } };
<script> import { ref, computed } from "vue"; export default { setup() { return useEventSpace(); // <--- Notice I've just extracted a function } }; function useEventSpace() { const capacity = ref(4); const attending = ref(["Tim", "Bob", "Joe"]); const spacesLeft = computed(() => { return capacity.value - attending.value.length; }); function increaseCapacity() { capacity.value++; } return { capacity, attending, spacesLeft, increaseCapacity }; } </script>
use/event-space.vue
file
import { ref, computed } from "vue"; export default function useEventSpace() { const capacity = ref(4); const attending = ref(["Tim", "Bob", "Joe"]); const spacesLeft = computed(() => { return capacity.value - attending.value.length; }); function increaseCapacity() { capacity.value++; } return { capacity, attending, spacesLeft, increaseCapacity }; }
<script> import useEventSpace from "@/use/event-space"; export default { setup() { return useEventSpace(); } }; </script>
<script> import useEventSpace from "@/use/event-space"; import useMapping from "@/use/mapping"; export default { setup() { const { capacity, attending, spacesLeft, increaseCapacity } = useEventSpace(); const { map, embedId } = useMapping(); return { capacity, attending, spacesLeft, increaseCapacity, map, embedId }; } </script>
In Vue 3’s Composition API we can create callback hooks inside setup() by adding "on" to the LifeCycle method name:
import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted, onActivated, onDeactivated, onErrorCaptured } from "vue"; export default { setup() { onBeforeMount(() => { console.log("Before Mount!"); }); onMounted(() => { console.log("Mounted!"); }); onBeforeUpdate(() => { console.log("Before Update!"); }); onUpdated(() => { console.log("Updated!"); }); onBeforeUnmount(() => { console.log("Before Unmount!"); }); onUnmounted(() => { console.log("Unmounted!"); }); onActivated(() => { console.log("Activated!"); }); onDeactivated(() => { console.log("Deactivated!"); }); onErrorCaptured(() => { console.log("Error Captured!"); }); } };
beforeCreate and created hooks are not needed when using the Composition API. This is because beforeCreate() is called right before setup() and created() is called right after setup(). Thus, we simply put code inside setup() that would normally be in these hooks, such as API calls.
setup
with props
<template> <p>{{ dayAndMonth }}</p> </template> <script> import { computed } from "@vue/composition-api"; export default { props: ["day", "month"], setup(props) { const dayAndMonth = computed(() => { return props.day + ', ' + props.month; }) return { dayAndMonth }; }, }; </script>
<input type="text" placeholder="City" v-model="city" >
import { watch } from "@vue/composition-api"; const city = ref(""); // Execute when city is changes watch(city, (newValue, oldValue) => { console.log('City: ', oldValue, newValue); });
<input type="text" placeholder="ID" v-model="personId" > <input type="text" placeholder="City" v-model="city" >
import { watch } from "@vue/composition-api"; const personId = ref(""); const city = ref(""); // Execute whenever city or personId are changes watch([city, personId], (newValues, oldValues) => { console.log('Updated array: ', oldValues, newValues); })
<template> <div> Search for <input v-model="searchInput" /> <div> <p>Number of events: {{ results }}</p> </div> </div> </template>
setup() { const searchInput = ref(""); const results = ref(0); watchEffect(() => { results.value = eventApi.getEventCount(searchInput.value); }); return { searchInput, results }; }