Vue
Skill ComeOnOliver/skillshub/skills/TerminalSkills/skills/vue
π§ The right skill, one API call. AI agent skills registry with token-efficient skill resolution. 5,000+ skills from 500+ top repos.From the repository description
npx -y skills add ComeOnOliver/skillshub --skill vueAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
SKILL.md
5.7 KB, ~1.4k tokens by cl100k_base, as published. Nobody here has run it
Vue
Vue 3 uses the Composition API for reactive state management, single-file components (.vue), and a virtual DOM with compiler optimizations. It scales from progressive enhancement to full SPAs.
Installation
# Create Vue project with Vite
npm create vue@latest my-app
cd my-app
npm install
npm run dev
Project Structure
# Vue 3 project layout
src/
βββ App.vue # Root component
βββ main.ts # Entry point
βββ router/index.ts # Vue Router config
βββ stores/ # Pinia stores
β βββ articles.ts
βββ composables/ # Reusable logic
β βββ useApi.ts
βββ components/ # Shared components
β βββ ArticleCard.vue
βββ views/ # Page components
β βββ HomeView.vue
β βββ ArticlesView.vue
βββ types/ # TypeScript types
βββ article.ts
Components (Composition API)
<!-- src/views/ArticlesView.vue β page component with script setup -->
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import type { Article } from '@/types/article'
import ArticleCard from '@/components/ArticleCard.vue'
const articles = ref<Article[]>([])
const loading = ref(true)
onMounted(async () => {
const res = await fetch('/api/articles')
articles.value = await res.json()
loading.value = false
})
</script>
<template>
<div>
<h1>Articles</h1>
<p v-if="loading">Loading...</p>
<div v-else>
<ArticleCard v-for="article in articles" :key="article.id" :article="article" />
</div>
</div>
</template>
<!-- src/components/ArticleCard.vue β reusable component with props -->
<script setup lang="ts">
import type { Article } from '@/types/article'
const props = defineProps<{ article: Article }>()
const emit = defineEmits<{ (e: 'delete', id: number): void }>()
</script>
<template>
<article class="card">
<RouterLink :to="`/articles/${props.article.slug}`">
<h2>{{ article.title }}</h2>
</RouterLink>
<p>{{ article.excerpt }}</p>
<button @click="emit('delete', article.id)">Delete</button>
</article>
</template>
Reactivity
<!-- src/components/Counter.vue β reactive state demo -->
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
const count = ref(0)
const doubled = computed(() => count.value * 2)
watch(count, (newVal) => {
console.log(`Count changed to ${newVal}`)
})
function increment() {
count.value++
}
</script>
<template>
<div>
<p>Count: {{ count }} (doubled: {{ doubled }})</p>
<button @click="increment">+1</button>
</div>
</template>
Pinia Store
// src/stores/articles.ts β Pinia state management
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import type { Article } from '@/types/article'
export const useArticlesStore = defineStore('articles', () => {
const articles = ref<Article[]>([])
const loading = ref(false)
const published = computed(() => articles.value.filter((a) => a.published))
async function fetchAll() {
loading.value = true
const res = await fetch('/api/articles')
articles.value = await res.json()
loading.value = false
}
async function create(data: Partial<Article>) {
const res = await fetch('/api/articles', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
const article = await res.json()
articles.value.unshift(article)
}
return { articles, loading, published, fetchAll, create }
})
Composables
// src/composables/useApi.ts β reusable fetch composable
import { ref, type Ref } from 'vue'
export function useApi<T>(url: string) {
const data: Ref<T | null> = ref(null)
const error = ref<string | null>(null)
const loading = ref(false)
async function execute() {
loading.value = true
error.value = null
try {
const res = await fetch(url)
if (!res.ok) throw new Error(`HTTP ${res.status}`)
data.value = await res.json()
} catch (e: any) {
error.value = e.message
} finally {
loading.value = false
}
}
return { data, error, loading, execute }
}
Router
// src/router/index.ts β Vue Router configuration
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/', component: () => import('@/views/HomeView.vue') },
{ path: '/articles', component: () => import('@/views/ArticlesView.vue') },
{ path: '/articles/:slug', component: () => import('@/views/ArticleView.vue'), props: true },
{
path: '/admin',
component: () => import('@/views/AdminView.vue'),
meta: { requiresAuth: true },
},
],
})
router.beforeEach((to) => {
if (to.meta.requiresAuth && !isAuthenticated()) {
return '/login'
}
})
export default router
Provide/Inject
// src/main.ts β provide global dependencies
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.provide('apiBase', '/api')
app.mount('#app')
Key Patterns
- Use
<script setup>for concise component definitions β it's the recommended style - Use
ref()for primitives,reactive()for objects;refis generally preferred - Use Pinia stores for shared state across components
- Extract reusable logic into composables (
use*functions) - Use
defineProps<T>()anddefineEmits<T>()for type-safe component interfaces - Lazy-load route components with dynamic
import()for code splitting
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.