Comprehensive Vue 3 Tutorial: Project Setup, Option vs Composition API, and Custom Hooks
This tutorial walks through creating a Vue 3 project, explains key changes and the differences between Option API and Composition API, demonstrates useful CLI commands, and provides detailed examples of custom composable hooks such as useWindowSize, usePagination, and useFetch, along with best practices.
Vue 3 Project Initialization
Run the terminal command to create a new Vue 3 project, then open http://127.0.0.1:5173 to see the project running. The generated directory structure includes public , src , assets , components , stores , router , and views folders.
Important Files and Folders
public : static assets that are not processed by the build tool; reference them with absolute paths like /favicon.ico .
assets : images, CSS, etc.
components : reusable UI components.
stores : Pinia state management.
router : Vue Router configuration.
views : route components.
NPM Scripts
npm run dev / npx vite : start a development server with Hot Module Replacement.
npm run build / npx vite build : bundle the project for production.
npm run preview / npx vite preview --port 4173 : preview the built dist folder locally.
Why Build?
Building performs syntax compilation, minification, third‑party module bundling, performance optimization, code protection, compatibility fixes, deployment convenience, gzip compression, and bundle analysis.
Option API vs. Composition API
Vue 3 offers both APIs. Option API follows the classic data , methods , computed , lifecycle sections, while Composition API groups logic by feature using reactive() , ref() , and <script setup> . Composition API improves readability for large components.
Option API Example
Logic is split into sections such as data , methods , etc.
Composition API Example
All logic can be placed inside <script setup> without a return statement.
Custom Composable Hooks
Reusable logic can be encapsulated in hooks:
useWindowSize : tracks window width and height.
usePagination : manages current page, builds paginated endpoint, provides nextPage and prevPage functions.
useFetch : performs async fetch, exposes data , loading , and error refs, supports reactive endpoint changes.
import { ref, computed, onMounted, isRef, watch } from "vue";
export function usePagination(endpoint) {
const currentPage = ref(1);
const paginatedEndpoint = computed(() => `${endpoint}?page=${currentPage.value}`);
function nextPage() { currentPage.value++; }
function prevPage() { if (currentPage.value > 1) currentPage.value--; }
return { endpoint: paginatedEndpoint, nextPage, prevPage };
}
export function useFetch(endpoint) {
const data = ref(null);
const loading = ref(true);
const error = ref(null);
function fetchData() {
loading.value = true;
return fetch(isRef(endpoint) ? endpoint.value : endpoint, {
method: "get",
headers: { "content-type": "application/json" }
})
.then(res => {
if (!res.ok) {
const err = new Error(res.statusText);
err.json = res.json();
throw err;
}
return res.json();
})
.then(json => { data.value = json; })
.catch(err => { error.value = err; if (err.json) { err.json.then(json => { error.value.message = json.message; }); } })
.finally(() => { loading.value = false; });
}
onMounted(() => { fetchData(); });
if (isRef(endpoint)) {
watch(endpoint, () => { fetchData(); });
}
return { data, loading, error };
}Usage in a component:
<script setup>
import { usePagination, useFetch } from './hooks';
const endpoint = 'https://example.com/api';
const { endpoint: paginatedEndpoint, nextPage, prevPage } = usePagination(endpoint);
const { data, loading, error } = useFetch(paginatedEndpoint);
function handleNextPage() { nextPage(); }
function handlePrevPage() { prevPage(); }
</script>
<template>
<div>
<button @click="handlePrevPage">Prev</button>
<button @click="handleNextPage">Next</button>
<div v-if="loading">Loading...</div>
<div v-if="data">Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.