Vue Composition API
Introduction to the
Why the Composition API? 🤔
👉 Limitations of Vue 2
👉 The Vue 3 solution
Limitations of Vue 2 🙈
Component readability & maintenance ⚠️
Let's see a live example 👀
export default Vue.extend({
...
data() {
return {
searchState: {
query: "vue",
loading: false,
results: []
} as SearchState<RawRepository>,
};
},
methods: {
runSearch(): void {
...
this.searchState.loading = true;
githubAPI
.get(`/search/repositories?q=${this.searchState.query}`)
.then((response: AxiosResponse<RawResult<RawRepository>>) => {
this.searchState.results = response.data.items;
})
.finally(() => {
this.searchState.loading = false;
});
}
},
mounted(): void {
this.runSearch();
}
});
export default Vue.extend({
...
data() {
return {
orderOptions: [
{ label: "🌡️ score", value: "score" },
{ label: "⭐ stargazers", value: "stargazers_count" },
{ label: "⚠️ issues", value: "open_issues_count" }
] as OrderOption[],
orderIndex: 0
};
},
computed: {
orderedResults(): RawRepository[] {
return orderBy(
this.searchState.results,
this.orderOptions[this.orderIndex].value,
"desc"
);
}
},
...
});
data() {
return {
searchState: {
query: "vue",
loading: false,
results: []
} as SearchState<RawRepository>,
orderOptions: [
{ label: "🌡️ score", value: "score" },
{ label: "⭐ stargazers", value: "stargazers_count" },
{ label: "⚠️ issues", value: "open_issues_count" }
] as OrderOption[],
orderIndex: 0
};
},
computed: {
orderedResults(): RawRepository[] {
return orderBy(
this.searchState.results,
this.orderOptions[this.orderIndex].value,
"desc"
);
}
},
methods: {
runSearch(): void {
...
this.searchState.loading = true;
githubAPI
.get(`/search/repositories?q=${this.searchState.query}`)
.then((response: AxiosResponse<RawResult<RawRepository>>) => {
this.searchState.results = response.data.items;
})
.finally(() => {
this.searchState.loading = false;
});
}
},
mounted(): void {
this.runSearch();
}
Features are organized by component options 🧐
Not by logical concerns...
Limitations of Vue 2 🙈
Limited code reuse patterns ⚠️
Mixins 🔎
export default function searchMixinFactory<T>({ ... }) {
return {
data() {
return {
searchState: {
query: "vue",
loading: false,
results: []
} as SearchState<T>
};
},
methods: {
runSearch(): void {
...
}
},
mounted() {
this.runSearch();
}
};
}
export default function orderMixinFactory<T>({ ... }) {
return {
data() {
return {
orderOptions: [
{ label: "🌡️ score", value: "score" },
{ label: "⭐ stargazers", value: "stargazers_count" },
{ label: "⚠️ issues", value: "open_issues_count" }
] as OrderOption[],
orderIndex: 0
};
},
computed: {
orderedResults(): T[] {
return orderBy(
this.searchState.results,
this.orderOptions[this.orderIndex].value,
"desc"
);
}
}
};
}
import { RawRepository } from "@/types";
import searchMixinFactory from "@/mixins/search";
import orderMixinFactory from "@/mixins/order";
export default Vue.extend({
mixins: [
searchMixinFactory<RawRepository>({ ... }),
orderMixinFactory<RawRepository>({ ... })
],
...
});
Mixins 🚨
⚔️ Can lead to conflicts
🔌 Not really reusable
🕶️ Obscure relationships
🤸♂️ Can't be dynamically added
Scoped slots 🔎
<template>
<GenericSearch :get-results="getRepositories" v-slot="{ query, results, run }">
<GenericOrder
:input="results"
:options="orderOptions"
v-slot="{ orderedResults }">
<input :value="query" ... />
<SearchRepository v-for="repository of orderedResults" ... />
</GenericOrder>
</GenericSearch>
</template>
<script>
export default Vue.extend({
...
methods: {
getRepositories(query): RawRepository[] {
return githubAPI
.get(`/search/repositories?q=${query}`)
.then((response: AxiosResponse<RawResult<RawRepository>>) => {
return response.data.items
});
}
}
});
</script>
<template>
<div>
<slot v-bind="{ query, results, run }" />
</div>
</template>
<script>
export default Vue.extend({
props: {
getResults: Function
},
data() {
return {
query: "",
results: []
};
},
methods: {
run(): void {
this.getResults(this.query).then(results => this.results = results);
}
}
});
</script>
<template>
<div>
<slot v-bind="{ orderedResults }" />
</div>
</template>
<script>
export default Vue.extend({
props: {
input: Array,
options: Array
},
computed: {
orderedResults() {
return orderBy(input, options[index].value, "desc");
}
}
});
</script>
Scoped slots 🚨
⚙️ Lots of configuration in the template
🤯 Hard to understand
🕵️ Decrease readability
🔒 Less flexible (props from children only available in template)
The Vue 3 solution 💥
The composition functions 🛠️
The setup function 🏠
Completely optional 💁
export default Vue.extend({
...
setup() {
// search feature
...
// order feature
...
// filter feature
...
// pagination feature
...
}
});
Should we do this? 🙈
Nope ✋👮
Use Composition Functions 😎
import useSearch from "@/use/search";
import useOrder from "@/use/order";
import useFilter from "@/use/filter";
import usePagination from "@/use/pagination";
export default Vue.extend({
...
setup() {
return {
...useSearch(),
...useOrder(),
...useFilter(),
...usePagination()
};
}
});
DEMO
THANK YOU! 🙏
Introduction to the Vue Composition API
By Nicolas Payot
Introduction to the Vue Composition API
- 899