Saltar a contenido

Vue.js / Nuxt - Guía de Entorno

Guía para configurar el entorno de desarrollo Vue.js/Nuxt para trabajar con Claude Code.

Resumen de Capacidades

Capacidad Herramientas
Framework Vue 3, Nuxt 3
Build Vite, Webpack
State Pinia, Vuex
Testing Vitest, Vue Test Utils, Cypress
Styling Tailwind, UnoCSS, Vuetify
Type Safety TypeScript, vue-tsc

Instalación

Node.js y pnpm

# Windows
winget install OpenJS.NodeJS.LTS
corepack enable
corepack prepare pnpm@latest --activate

# macOS
brew install node@22
corepack enable

# Verificar
node --version   # 22.x+
pnpm --version   # 9.x+

Vue CLI (Opcional)

# CLI oficial (menos usado con Vite)
npm install -g @vue/cli

vue --version
vue create my-app

Crear Proyectos

Vue 3 + Vite (Recomendado)

# Crear proyecto
pnpm create vue@latest

# Opciones recomendadas:
# - TypeScript: Yes
# - JSX: No (opcional)
# - Vue Router: Yes
# - Pinia: Yes
# - Vitest: Yes
# - E2E Testing: Cypress
# - ESLint: Yes
# - Prettier: Yes

# Instalar dependencias
cd my-vue-app
pnpm install

# Desarrollo
pnpm dev

Nuxt 3 (Full-stack)

# Crear proyecto Nuxt
pnpm dlx nuxi@latest init my-nuxt-app

cd my-nuxt-app
pnpm install

# Desarrollo
pnpm dev

# Build
pnpm build
pnpm preview

Estructura de Proyecto

Vue 3 + Vite

my-vue-app/
├── public/
│   └── favicon.ico
├── src/
│   ├── assets/
│   ├── components/
│   │   └── HelloWorld.vue
│   ├── composables/        # Composition API hooks
│   ├── stores/             # Pinia stores
│   ├── views/              # Páginas/vistas
│   ├── router/
│   │   └── index.ts
│   ├── App.vue
│   └── main.ts
├── tests/
│   ├── unit/
│   └── e2e/
├── index.html
├── vite.config.ts
├── tsconfig.json
├── package.json
└── pnpm-lock.yaml

Nuxt 3

my-nuxt-app/
├── .nuxt/                  # Build output
├── assets/
├── components/             # Auto-import
├── composables/            # Auto-import
├── content/                # Nuxt Content
├── layouts/
├── middleware/
├── pages/                  # File-based routing
├── plugins/
├── public/
├── server/                 # API routes (Nitro)
│   ├── api/
│   └── middleware/
├── stores/
├── utils/                  # Auto-import
├── app.vue
├── nuxt.config.ts
├── package.json
└── tsconfig.json

Composables (Composition API)

// composables/useCounter.ts
import { ref, computed } from 'vue'

export function useCounter(initial = 0) {
  const count = ref(initial)

  const doubled = computed(() => count.value * 2)

  function increment() {
    count.value++
  }

  function decrement() {
    count.value--
  }

  return {
    count,
    doubled,
    increment,
    decrement
  }
}
<!-- Uso en componente -->
<script setup lang="ts">
import { useCounter } from '@/composables/useCounter'

const { count, doubled, increment } = useCounter(10)
</script>

<template>
  <div>
    <p>Count: {{ count }}</p>
    <p>Doubled: {{ doubled }}</p>
    <button @click="increment">+</button>
  </div>
</template>

State Management (Pinia)

// stores/user.ts
import { defineStore } from 'pinia'

interface User {
  id: string
  name: string
  email: string
}

export const useUserStore = defineStore('user', {
  state: () => ({
    user: null as User | null,
    isAuthenticated: false
  }),

  getters: {
    displayName: (state) => state.user?.name ?? 'Guest'
  },

  actions: {
    async login(email: string, password: string) {
      // API call
      const response = await fetch('/api/login', {
        method: 'POST',
        body: JSON.stringify({ email, password })
      })
      const user = await response.json()
      this.user = user
      this.isAuthenticated = true
    },

    logout() {
      this.user = null
      this.isAuthenticated = false
    }
  }
})
<!-- Uso en componente -->
<script setup lang="ts">
import { useUserStore } from '@/stores/user'

const userStore = useUserStore()
</script>

<template>
  <div v-if="userStore.isAuthenticated">
    <p>Welcome, {{ userStore.displayName }}</p>
    <button @click="userStore.logout">Logout</button>
  </div>
</template>

Testing

Vitest (Unit Tests)

// tests/unit/Counter.spec.ts
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import Counter from '@/components/Counter.vue'

describe('Counter', () => {
  it('renders initial count', () => {
    const wrapper = mount(Counter, {
      props: { initialCount: 5 }
    })
    expect(wrapper.text()).toContain('5')
  })

  it('increments on click', async () => {
    const wrapper = mount(Counter)
    await wrapper.find('button').trigger('click')
    expect(wrapper.text()).toContain('1')
  })
})
# Ejecutar tests
pnpm test:unit
pnpm vitest
pnpm vitest --coverage

Cypress (E2E)

// cypress/e2e/home.cy.ts
describe('Home Page', () => {
  it('displays welcome message', () => {
    cy.visit('/')
    cy.contains('Welcome')
  })

  it('navigates to about', () => {
    cy.visit('/')
    cy.get('[data-cy="nav-about"]').click()
    cy.url().should('include', '/about')
  })
})
# Cypress
pnpm cypress:open
pnpm cypress:run

Nuxt 3 Features

Server API Routes

// server/api/users/[id].ts
export default defineEventHandler(async (event) => {
  const id = getRouterParam(event, 'id')

  // Fetch from database
  const user = await getUserById(id)

  if (!user) {
    throw createError({
      statusCode: 404,
      message: 'User not found'
    })
  }

  return user
})

Data Fetching

<script setup lang="ts">
// useFetch - SSR-friendly
const { data: users, pending, error } = await useFetch('/api/users')

// useAsyncData - más control
const { data: posts } = await useAsyncData('posts', () =>
  $fetch('https://api.example.com/posts')
)

// useLazyFetch - no bloquea navegación
const { data: comments, pending: loading } = useLazyFetch('/api/comments')
</script>

Middleware

// middleware/auth.ts
export default defineNuxtRouteMiddleware((to, from) => {
  const user = useUserStore()

  if (!user.isAuthenticated && to.path !== '/login') {
    return navigateTo('/login')
  }
})

UI Libraries

Vuetify 3

# Instalar
pnpm add vuetify

# plugins/vuetify.ts
import { createVuetify } from 'vuetify'
import * as components from 'vuetify/components'
import * as directives from 'vuetify/directives'
import 'vuetify/styles'

export default createVuetify({
  components,
  directives,
  theme: {
    defaultTheme: 'dark'
  }
})

Tailwind CSS

pnpm add -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
// tailwind.config.js
export default {
  content: [
    './index.html',
    './src/**/*.{vue,js,ts,jsx,tsx}'
  ],
  theme: {
    extend: {}
  },
  plugins: []
}

UnoCSS (Nuxt)

pnpm add -D @unocss/nuxt
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@unocss/nuxt'],
  unocss: {
    preflight: true
  }
})

Comandos que Claude Code Ejecutará

# Crear proyectos
pnpm create vue@latest
pnpm dlx nuxi init my-app

# Desarrollo
pnpm dev
pnpm build
pnpm preview

# Testing
pnpm test:unit
pnpm vitest
pnpm cypress:open
pnpm cypress:run

# Linting
pnpm lint
pnpm lint:fix
vue-tsc --noEmit

# Dependencias
pnpm add package-name
pnpm add -D dev-package
pnpm update

VS Code Extensions

code --install-extension Vue.volar
code --install-extension Vue.vscode-typescript-vue-plugin
code --install-extension dbaeumer.vscode-eslint
code --install-extension esbenp.prettier-vscode
code --install-extension bradlc.vscode-tailwindcss
code --install-extension antfu.unocss

Verificación del Entorno

#!/bin/bash
echo "=== Verificación Entorno Vue ==="

echo -e "\n--- Runtime ---"
node --version
pnpm --version

echo -e "\n--- Vue ---"
pnpm dlx vue --version 2>/dev/null || echo "Vue CLI no global"

echo -e "\n--- Nuxi ---"
pnpm dlx nuxi --version 2>/dev/null || echo "Nuxi disponible via pnpm dlx"

echo -e "\n=== Verificación Completa ==="

Recursos