Frontend Development 19 min read

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.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Comprehensive Vue 3 Tutorial: Project Setup, Option vs Composition API, and Custom Hooks

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">
frontendComposition APIviteVue3Custom HooksOption API
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.