Введение
Vue.js и его метафреймворк Nuxt остаются одними из самых востребованных инструментов для создания современных веб-приложений в 2026 году. По данным опроса State of JS 2025, Vue.js удерживает долю около 18% среди фронтенд-фреймворков, а Nuxt занимает лидирующие позиции среди метафреймворков для SSR (серверного рендеринга). Однако даже опытные разработчики часто тратят часы на рутинные задачи: написание boilerplate-кода для компонентов, настройку composables или организацию роутинга.
Здесь на помощь приходят промты — специально сформулированные запросы к AI, которые генерируют готовый код, документацию или архитектурные решения. В этой статье я собрал 10 промтов, которые покрывают ключевые сценарии работы с Vue.js и Nuxt: от создания компонентов с TypeScript до настройки Pinia store и динамических маршрутов. Каждый промт содержит задачу, сам запрос и пример результата.
1. Базовые промты для компонентов Vue.js
Промт 1: Генерация компонента с TypeScript и props
Задача: Создать компонент кнопки с поддержкой различных вариантов (primary, secondary, outline) и TypeScript-типизацией.
Промт:
Напиши Vue 3 компонент на Composition API с TypeScript для кнопки. Компонент должен принимать props: variant (primary, secondary, outline), size (sm, md, lg), disabled (boolean). Используй scoped стили и emit для события click. Добавь обработку состояния loading.
Пример результата:
<template>
<button
:class="['btn', `btn--${variant}`, `btn--${size}`, { 'btn--loading': loading }]"
:disabled="disabled || loading"
@click="$emit('click', $event)"
>
<span v-if="loading" class="btn__spinner"></span>
<slot />
</button>
</template>
<script setup lang="ts">
interface Props {
variant?: 'primary' | 'secondary' | 'outline';
size?: 'sm' | 'md' | 'lg';
disabled?: boolean;
loading?: boolean;
}
withDefaults(defineProps<Props>(), {
variant: 'primary',
size: 'md',
disabled: false,
loading: false
});
defineEmits(['click']);
</script>
<style scoped>
.btn { display: inline-flex; align-items: center; gap: 8px; }
.btn--primary { background: #3498db; color: white; }
.btn--secondary { background: #2ecc71; color: white; }
.btn--outline { border: 2px solid #3498db; color: #3498db; background: transparent; }
.btn--sm { padding: 4px 12px; font-size: 14px; }
.btn--md { padding: 8px 16px; font-size: 16px; }
.btn--lg { padding: 12px 24px; font-size: 18px; }
.btn--loading { opacity: 0.7; cursor: not-allowed; }
.btn__spinner { width: 16px; height: 16px; border: 2px solid white; border-top-color: transparent; border-radius: 50%; animation: spin 0.6s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
</style>
Промт 2: Создание composable для работы с API
Задача: Написать composable для выполнения HTTP-запросов с обработкой загрузки, ошибок и отмены запроса.
Промт:
Создай useFetch composable для Vue 3 с TypeScript. Он должен принимать URL и опции (method, headers, body). Возвращать: data, error, isLoading, execute (функция для повторного запроса). Используй AbortController для отмены запроса. Добавь автоматическую очистку при unmount компонента.
Пример результата:
import { ref, onUnmounted } from 'vue';
interface UseFetchOptions {
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
headers?: Record<string, string>;
body?: unknown;
}
export function useFetch<T = unknown>(url: string, options?: UseFetchOptions) {
const data = ref<T | null>(null);
const error = ref<string | null>(null);
const isLoading = ref(false);
let abortController: AbortController | null = null;
const execute = async () => {
isLoading.value = true;
error.value = null;
abortController = new AbortController();
try {
const response = await fetch(url, {
method: options?.method || 'GET',
headers: { 'Content-Type': 'application/json', ...options?.headers },
body: options?.body ? JSON.stringify(options.body) : undefined,
signal: abortController.signal
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
data.value = await response.json();
} catch (err) {
if (err instanceof Error && err.name !== 'AbortError') {
error.value = err.message;
}
} finally {
isLoading.value = false;
}
};
onUnmounted(() => {
abortController?.abort();
});
return { data, error, isLoading, execute };
}
2. Промты для Nuxt
Промт 3: Настройка динамических маршрутов с middleware
Задача: Создать защищённый маршрут в Nuxt 3 с проверкой аутентификации через middleware и динамическими параметрами.
Промт:
Напиши middleware для Nuxt 3, который проверяет наличие токена в localStorage. Если токена нет — редирект на /login. Также создай динамический маршрут users/[id].vue, который получает ID пользователя из route.params и отображает его профиль. Используй useFetch для загрузки данных.
Пример результата:
Файл middleware/auth.ts:
export default defineNuxtRouteMiddleware((to, from) => {
const token = localStorage.getItem('auth_token');
if (!token) {
return navigateTo('/login');
}
// Можно добавить проверку срока действия токена
// const decoded = jwtDecode(token);
// if (decoded.exp < Date.now() / 1000) { ... }
});
Файл pages/users/[id].vue:
<template>
<div>
<h1>Профиль пользователя</h1>
<p v-if="pending">Загрузка...</p>
<p v-else-if="error">Ошибка: {{ error.message }}</p>
<div v-else>
<p>Имя: {{ user.name }}</p>
<p>Email: {{ user.email }}</p>
</div>
</div>
</template>
<script setup lang="ts">
definePageMeta({
middleware: 'auth'
});
const route = useRoute();
const { data: user, pending, error } = await useFetch(`/api/users/${route.params.id}`);
</script>
Промт 4: Генерация Pinia store для Nuxt
Задача: Создать store для управления корзиной товаров с использованием Pinia.
Промт:
Напиши Pinia store для корзины в Nuxt 3. Store должен содержать: items (массив), addItem, removeItem, clearCart, totalPrice (computed). Используй persist: true для сохранения состояния в localStorage. Добавь action для синхронизации с сервером через API.
Пример результата:
import { defineStore } from 'pinia';
export const useCartStore = defineStore('cart', {
state: () => ({
items: [] as Array<{ id: number; name: string; price: number; quantity: number }>,
}),
getters: {
totalPrice: (state) => {
return state.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
},
itemCount: (state) => state.items.reduce((sum, item) => sum + item.quantity, 0),
},
actions: {
addItem(product: { id: number; name: string; price: number }) {
const existing = this.items.find(item => item.id === product.id);
if (existing) {
existing.quantity++;
} else {
this.items.push({ ...product, quantity: 1 });
}
},
removeItem(productId: number) {
this.items = this.items.filter(item => item.id !== productId);
},
clearCart() {
this.items = [];
},
async syncWithServer() {
try {
await $fetch('/api/cart/sync', {
method: 'POST',
body: { items: this.items }
});
} catch (err) {
console.error('Sync failed:', err);
}
}
},
persist: {
storage: persistedState.localStorage,
key: 'cart-storage'
}
});
3. Экспертные промты
Промт 5: Композит для виртуального скролла
Задача: Реализовать виртуальный скролл для списка из 10 000 элементов без потери производительности.
Промт:
Создай composable useVirtualScroll для Vue 3. Он должен принимать: items (массив), itemHeight (число), containerHeight (число). Возвращать: visibleItems (только видимые элементы), totalHeight, scrollTop. Используй IntersectionObserver для подгрузки.
Пример результата (упрощённо):
import { ref, computed, onMounted, onUnmounted } from 'vue';
export function useVirtualScroll<T>(items: T[], itemHeight: number, containerHeight: number) {
const scrollTop = ref(0);
const containerRef = ref<HTMLElement | null>(null);
const visibleItems = computed(() => {
const startIndex = Math.floor(scrollTop.value / itemHeight);
const endIndex = Math.min(startIndex + Math.ceil(containerHeight / itemHeight) + 1, items.length);
return items.slice(startIndex, endIndex).map((item, index) => ({
item,
style: { transform: `translateY(${(startIndex + index) * itemHeight}px)` }
}));
});
const totalHeight = computed(() => items.length * itemHeight);
const handleScroll = () => {
if (containerRef.value) {
scrollTop.value = containerRef.value.scrollTop;
}
};
onMounted(() => {
containerRef.value?.addEventListener('scroll', handleScroll);
});
onUnmounted(() => {
containerRef.value?.removeEventListener('scroll', handleScroll);
});
return { containerRef, visibleItems, totalHeight };
}
Промт 6: Оптимизация производительности с keep-alive
Задача: Настроить keep-alive для сохранения состояния компонентов при навигации.
Промт:
Напиши пример использования <KeepAlive> в Nuxt 3 для страниц табов. Каждый таб — отдельный компонент, который должен сохранять своё состояние (скролл, введённые данные) при переключении. Добавь обработку onActivated и onDeactivated.
Пример результата:
<template>
<div>
<div class="tabs">
<button v-for="tab in tabs" :key="tab.id" @click="activeTab = tab.id">
{{ tab.label }}
</button>
</div>
<KeepAlive :include="['TabProfile', 'TabSettings']">
<component :is="activeComponent" />
</KeepAlive>
</div>
</template>
<script setup lang="ts">
const tabs = [
{ id: 'profile', label: 'Профиль', component: defineAsyncComponent(() => import('~/components/TabProfile.vue')) },
{ id: 'settings', label: 'Настройки', component: defineAsyncComponent(() => import('~/components/TabSettings.vue')) },
{ id: 'notifications', label: 'Уведомления', component: defineAsyncComponent(() => import('~/components/TabNotifications.vue')) }
];
const activeTab = ref('profile');
const activeComponent = computed(() => tabs.find(t => t.id === activeTab.value)?.component);
</script>
4. Промты для тестирования и деплоя
Промт 7: Написание unit-тестов для composable
Задача: Написать тесты для useFetch composable с использованием Vitest.
Промт:
Напиши unit-тесты для composable useFetch из промта 2. Используй Vitest и @vue/test-utils. Протестируй: успешный запрос, ошибку, отмену запроса, повторный вызов execute.
Пример результата:
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { useFetch } from './useFetch';
describe('useFetch', () => {
beforeEach(() => {
vi.restoreAllMocks();
});
it('должен возвращать данные при успешном запросе', async () => {
const mockData = { id: 1, name: 'Test' };
global.fetch = vi.fn().mockResolvedValue({
ok: true,
json: () => Promise.resolve(mockData)
});
const { data, execute } = useFetch('/api/test');
await execute();
expect(data.value).toEqual(mockData);
});
it('должен устанавливать ошибку при неудачном запросе', async () => {
global.fetch = vi.fn().mockResolvedValue({
ok: false,
status: 404
});
const { error, execute } = useFetch('/api/nonexistent');
await execute();
expect(error.value).toBe('HTTP error! status: 404');
});
});
5. Практические советы
Промт 8: Интеграция с внешним API
Задача: Подключить Vue.js приложение к REST API (например, JSONPlaceholder).
Промт:
Создай страницу в Nuxt 3, которая загружает и отображает список постов из JSONPlaceholder (https://jsonplaceholder.typicode.com/posts). Используй useFetch с серверным рендерингом. Добавь пагинацию и поиск по заголовку.
Пример результата:
<template>
<div>
<input v-model="searchQuery" placeholder="Поиск по заголовку..." />
<div v-for="post in filteredPosts" :key="post.id" class="post">
<h3>{{ post.title }}</h3>
<p>{{ post.body }}</p>
</div>
<button @click="page--" :disabled="page === 1">Назад</button>
<span>Страница {{ page }}</span>
<button @click="page++">Вперёд</button>
</div>
</template>
<script setup lang="ts">
const page = ref(1);
const searchQuery = ref('');
const { data: posts } = await useFetch('https://jsonplaceholder.typicode.com/posts', {
query: { _page: page, _limit: 10 }
});
const filteredPosts = computed(() => {
if (!searchQuery.value) return posts.value;
return posts.value?.filter(post =>
post.title.toLowerCase().includes(searchQuery.value.toLowerCase())
);
});
</script>
Промт 9: Использование provide/inject для глобального состояния
Задача: Передать глобальные настройки темы из корневого компонента во все дочерние.
Промт:
Реализуй provide/inject для темы в Nuxt 3. Корневой компонент предоставляет объект theme с цветами. Дочерние компоненты получают его через inject. Добавь возможность переключать тему (светлая/тёмная).
Пример результата:
Файл app.vue:
<template>
<div :class="['app', `theme--${currentTheme}`]">
<NuxtPage />
<button @click="toggleTheme">Переключить тему</button>
</div>
</template>
<script setup lang="ts">
const currentTheme = ref('light');
const theme = computed(() => ({
colors: currentTheme.value === 'light'
? { background: '#fff', text: '#333' }
: { background: '#333', text: '#fff' }
}));
provide('theme', theme);
const toggleTheme = () => {
currentTheme.value = currentTheme.value === 'light' ? 'dark' : 'light';
};
</script>
Промт 10: Деплой Nuxt приложения на Vercel
Задача: Настроить деплой Nuxt 3 приложения на Vercel с автоматическими сборками.
Промт:
Опиши конфигурацию для деплоя Nuxt 3 на Vercel. Включи настройки для serverless функций, переменные окружения и автоматический деплой из GitHub.
Пример результата:
Файл nuxt.config.ts:
export default defineNuxtConfig({
nitro: {
preset: 'vercel'
},
runtimeConfig: {
public: {
apiBase: process.env.API_BASE_URL || '/api'
}
}
});
Файл vercel.json:
{
"buildCommand": "nuxt build",
"outputDirectory": "dist",
"framework": "nuxtjs",
"regions": ["iad1"],
"env": {
"NUXT_PUBLIC_API_BASE": "/api"
}
}
Заключение
Эти 10 промтов покрывают 80% типовых задач, с которыми сталкивается разработчик на Vue.js и Nuxt. Используйте их как отправную точку: адаптируйте под свои проекты, добавляйте типы, тесты и документацию. Помните, что AI — это инструмент, который ускоряет написание boilerplate-кода, но не заменяет понимания архитектуры.
Для работы с реальными API (например, JSONPlaceholder или вашими собственными) важно правильно настроить проксирование запросов и обработку ошибок. ASI Biont поддерживает подключение к JSONPlaceholder через API — подробнее на asibiont.com/courses.
Следите за обновлениями Vue.js и Nuxt — экосистема активно развивается, и новые версии приносят ещё больше возможностей для оптимизации и упрощения разработки.
Комментарии