vueschool.io
@vueschool_io
new Vue({
data: {
loading: false,
count: 0,
user: {}
},
computed: {
double () { return this.count * 2 },
fullname () {/* ... */}
},
methods: {
increment () { this.count++ },
fetchUser () {/* ... */}
}
})
π advanced feature
π options API is not being deprecated
β addition to the current API
a set of lower level APIs
use Vue features outside of Vue components
import {ref, computed, watch} from 'vue'
the setup option
Vue Components
using setup
new Vue({
beforeCreate () {
},
setup () {
},
created () {
}
})
the setup option
the setup option
<template>
<button @click="increaseCount">count {{count}} | double {{double}}</button>
</template>
<script>
import {computed, ref} from 'vue'
export default {
setup () {
const count = ref(0)
const double = computed(() => count.value * 2)
const increaseCount = () => {
count.value++
}
return {increaseCount, count, double} // render context - unwrapped
},
mounted () {
this.count = 40
this.increaseCount()
}
}
</script>
the setup option
ButtonCounter.vue
Teacher @ Vue School
Full Stack developer (10 years)
Husband and Father
setup(){
//...
return {
characters,
loadingState,
fetchAllCharacters
};
}
<script setup>
compile-time syntactic sugar for using Composition API inside Single-File Components (SFCs)
<script setup>
import { ref } from "vue";
const characters = ref([]);
const loadingState = ref(null);
function fetchAllCharacters() {
//...
}
</script>
<script setup>
//...
</script>
<template>
//...
</template>
<script>
import HelloWorld from "@/components/HelloWorld.vue";
export default {
components: {
HelloWorld,
},
};
</script>
<script setup>
import HelloWorld from "@/components/HelloWorld.vue";
</script>
<script setup>
// enables v-focus in templates
const vFocus = {
mounted: (el) => el.focus()
}
</script>
<template>
<input v-focus />
</template>
β»οΈ Logic Reuse
π Code Organization
π Improved TypeScript Support
import axios from 'axios'
import orderBy from 'lodash.orderby'
export default {
data () {
return {
characters: [],
loadingState: null,
orderKey: 'id'
}
},
computed: {
charactersOrdered() {
return orderBy(this.characters, this.orderKey)
}
},
methods: {
fetchAllCharacters () {
this.loadingState = 'loading'
axios.get('https://rickandmortyapi.com/api/character')
.then(response => {
setTimeout(() => {
this.loadingState = 'success'
this.characters = response.data.results
}, 1000)
})
},
setOrderKey(key) {
this.orderKey = key
}
},
created () {
this.fetchAllCharacters()
}
}
<script setup>
import axios from 'axios'
import orderBy from 'lodash/orderby'
import {computed, ref} from 'vue'
const characters = ref([])
const loadingState = ref(null)
const fetchAllCharacters = () => {
loadingState.value = 'loading'
return axios.get('https://rickandmortyapi.com/api/character')
.then(response => {
loadingState.value = 'success'
characters.value = response.data.results
})
}
fetchAllCharacters()
const orderKey = ref('id')
const charactersOrdered = computed(() => {
return orderBy(characters.value, orderKey.value)
})
const setOrderKey = (key) => {
orderKey.value = key
}
</script>
<template>
<button @click="increaseCount">count {{count}} | double {{double}}</button>
</template>
<script>
import {computed, ref} from 'vue'
export default {
setup () {
const count = ref(0)
const double = computed(() => count.value * 2)
const increaseCount = () => {
count.value++
}
return {increaseCount, count, double} // render context - unwrapped
},
mounted () {
this.count = 40
this.increaseCount()
}
}
</script>
Button Component From Earlier
<script>
import {computed, ref} from 'vue'
const useCounter = () => {
const count = ref(0)
const double = computed(() => count.value * 2)
const increaseCount = () => {
count.value++
}
return {count, double, increaseCount}
}
export default {
setup () {
const {increaseCount, count, double} = useCounter()
return {increaseCount, count, double} // render context
}
}
</script>
<script>
import {computed, ref} from 'vue'
const useCounter = (initial = 0) => {
const count = ref(initial)
const double = computed(() => count.value * 2)
const increaseCount = () => {
count.value++
}
return {count, double, increaseCount}
}
export default {
setup () {
const {increaseCount, count, double} = useCounter(40)
return {increaseCount, count, double}
}
}
</script>
// src/composables/useCounter.js
import {computed, ref} from 'vue'
export const useCounter = (initial = 0) => {
const count = ref(initial)
const double = computed(() => count.value * 2)
const increaseCount = () => {
count.value++
}
return {count, double, increaseCount}
}
Can extract to another file
// src/composables/useCounter.js
import {computed, ref} from 'vue'
export const useCounter = (initial = 0) => {
const count = ref(initial)
const double = computed(() => count.value * 2)
const increaseCount = () => {
count.value++
}
return {count, double, increaseCount}
}
Can extract to another file
// CounterButton.vue
<script>
import {useCounter} from "@/composables/useCounter"
export default {
setup () {
const {increaseCount, count, double} = useCounter(40)
return {increaseCount, count, double}
}
}
</script>
// src/composables/useCounter.js
import {computed, ref} from 'vue'
export const useCounter = (initial = 0) => {
const count = ref(initial)
const double = computed(() => count.value * 2)
const increaseCount = () => {
count.value++
}
return {count, double, increaseCount}
}
Can extract to another file
// CounterButton.vue
<script setup>
import {useCounter} from "@/composables/useCounter"
const {increaseCount, count, double} = useCounter(40)
</script>
// /src/composables/useFetchResource.js
import {ref} from 'vue'
import axios from 'axios'
export default function useFetchResource (url) {
const data = ref([])
const loadingState = ref(null)
const fetchResource = () => {
loadingState.value = 'loading'
return axios.get(url)
.then(response => {
loadingState.value = 'success'
data.value = response.data.results
})
}
return {data, loadingState, fetchResource}
}
<script setup>
// ...
const characters = ref([]);
const loadingState = ref(null);
function fetchAllCharacters() {
loadingState.value = "loading";
axios.get("https://rickandmortyapi.com/api/character").then((response) => {
setTimeout(() => {
loadingState.value = "success";
characters.value = response.data.results;
}, 1000);
});
}
fetchAllCharacters();
</script>
CharacterCards.vue
<script setup>
import { useFetchResource } from "@/composables/useFetchResource";
// ...
const {
data: characters,
fetchResource: fetchAllCharacters,
loadingState,
} = useFetchResource("https://rickandmortyapi.com/api/character");
fetchAllCharacters();
</script>
CharacterCards.vue
<template></template>
<script>
import useMousePosition from '@/composables/useMousePosition'
export default {
setup () {
const {x, y} = useMousePosition()
// ...
}
}
</script>
Respective Drawbacks when compared with the Composition API
// MyComponent.vue
<script setup>
import { onMounted, onUnmounted } from "vue";
onMounted(()=>{
// do things
})
</script>
// useMyComposable.js
import { onMounted, onUnmounted } from "vue";
export function useMyComposable(){
onMounted(()=>{
// do things
})
}
<script>
export default {
props: ['foo'],
setup(props) {
// setup() receives props as the first argument.
console.log(props.foo)
}
}
</script>
<script setup>
const props = defineProps({
foo: String,
});
console.log(props.foo)
</script>
<script>
export default {
emits: ['inFocus', 'submit'],
setup(props, ctx) {
ctx.emit('submit')
}
}
</script>
<script setup>
const emit = defineEmits([
'inFocus',
'submit'
])
emit('submit')
</script>
<script>
export default{
onMounted(){
this.$refs.input.focus()
}
}
</script>
<template>
<input ref="input" />
</template>
<script setup>
import { ref, onMounted } from 'vue'
// declare a ref to hold the element reference
// the name must match template ref value
const input = ref(null)
onMounted(() => {
input.value.focus()
})
</script>
<template>
<input ref="input" />
</template>
<script setup>
const framework = reactive({
name: 'Vue',
author:'Evan You',
tags: ['javascript', 'vue']
})
framework.name // no need for .value
</script>
Alternative way to declare reactive data
// β
ref works
const framwork = ref('Vue')
const isAwesome = ref(true)
const created = ref(2014)
Ref Works on Primitives
Reactive does not
// β won't work
const framwork = reactive('Vue')
const isAwesome = reactive(true)
const created = reactive(2014)
// β
ref works
let posts = ref(['post 1', 'post 2'])
posts.value = ['post 3', 'post 4']
Can Re-assign whole ref values but not reactive
// β won't work
let posts = reactive(['post 1', 'post 2'])
posts = ['post 3', 'post 4']
// ref
const framework = ref({
name: 'Vue',
author:'Evan You',
tags: ['javascript', 'vue']
})
β¬οΈ
console.log(framework.value.name)
Ref requires .value reactive does not
// reactive
const framework = reactive({
name: 'Vue',
author:'Evan You',
tags: ['javascript', 'vue']
})
console.log(framework.name)
// ref
const {name} = toRefs(reactive({
name: 'Vue',
author:'Evan You',
tags: ['javascript', 'vue']
}))
console.log(name.value)
Β Can convert reactive object to refsΒ
Can convert reactive object to refs
Courses to Dig Deeper into the Composition API
Courses to learn further Vue
Other Great Courses too!
NEW
πΊ
Vue Video
Courses
π¨βπ«
Live Workshops
π¬
Bi-weekly Support
Sessions
π§βπ§
Database for hiring
Vue devs
Our goal
800+ Video Lessons
140000 users
Alex Kyriakidis
Daniel Kelly
Debbie O'Brien
Chris Fritz
Maria Lamardo
Roman Kuba
Anthony Fu
Filip Rakowski
Alexander Lichter
Lydia Hallie
Interested? Talk to our team at the conference or send us an email
π team@vueschool.io
Interested? Talk to our team at the conference or send us an email
π team@vueschool.io
π₯ Vue.js Berlin Special Offer π₯