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"><!-- display data --></div>
<div v-if="error">Error: {{ error }}</div>
</div>
</template>Advanced Script Features
setup()vs. <script setup> – the latter is syntactic sugar that removes the need for a return statement. defineProps and defineEmits – compiler macros for declaring props and events without imports.
Multiple <script> blocks can be merged during compilation. defineOptions (Vue 3.3+): declare component options directly inside <script setup>. defineExpose: expose internal methods or values to parent components. defineSlots (Vue 3.3+): provide IDE slot type hints. Suspense: handle async components with fallback UI.
Single File Components (SFC)
SFCs combine <template>, <script> (or <script setup>) and <style> sections in a single .vue file, which is compiled by @vue/compiler-sfc into JavaScript and CSS for the browser.
References
Vue Chinese official documentation and community resources.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
