10 промтов для Vue.js и Nuxt: компоненты, composables, роутинг

Vue.js — один из самых популярных фронтенд-фреймворков (по данным State of JS 2025, его используют 49% разработчиков). Nuxt 3 добавляет серверный рендеринг, file-based routing и автоматические composables. Но даже опытные разработчики тратят часы на шаблонный код: настройка Pinia store, создание компонентов с TypeScript, типизация роутов.

Чтобы ускорить разработку, я собрал 10 промтов для AI-ассистентов (ChatGPT, Claude, Gemini). Каждый промт — готовый шаблон для конкретной задачи: от генерации компонента до миграции Options API на Composition API. Вы просто копируете промт, вставляете в AI и получаете код, который можно сразу использовать в проекте.

1. Генерация компонента с TypeScript и Composition API

Для чего: Создать компонент с пропсами, emits и слотами, используя <script setup lang="ts">.

Промт:

Сгенерируй Vue 3 компонент на Composition API с TypeScript. Компонент должен:
- Принимать пропсы: title (string, required), items (array of {id: number, name: string}, default: []), loading (boolean, default: false)
- Эмитить события: 'update:title' (with string), 'select' (with number)
- Иметь слот default и слот 'footer'
- Использовать v-model через computed
- Не использовать Options API
- Добавить стили scoped
- Импортировать defineProps, defineEmits, computed из 'vue'

Пример использования:

<script setup lang="ts">
import { computed } from 'vue'

interface Item {
  id: number
  name: string
}

const props = withDefaults(defineProps<{
  title: string
  items?: Item[]
  loading?: boolean
}>(), {
  items: () => [],
  loading: false
})

const emit = defineEmits<{
  (e: 'update:title', value: string): void
  (e: 'select', id: number): void
}>()

const localTitle = computed({
  get: () => props.title,
  set: (val: string) => emit('update:title', val)
})
</script>

2. Композабл для работы с API (useFetch с кэшированием)

Для чего: Создать composable для запросов к бэкенду с автоматическим кэшированием и обработкой ошибок.

Промт:

Напиши composable useApi для Nuxt 3, который:
- Принимает URL и опции (method, body, headers)
- Использует useFetch из Nuxt
- Кэширует результат на 5 минут через ключ в sessionStorage
- Возвращает { data, error, pending, refresh }
- Добавь очистку кэша по ключу
- Обрабатывает ошибки сети и парсинга JSON
- Используй TypeScript с дженериками

Пример:

export function useApi<T>(url: string, options?: { method?: string; body?: any; headers?: Record<string, string> }) {
  const cacheKey = `api_${url}_${JSON.stringify(options)}`
  const cached = sessionStorage.getItem(cacheKey)

  if (cached) {
    return { data: ref(JSON.parse(cached)), error: ref(null), pending: ref(false), refresh: () => {} }
  }

  const { data, error, pending, refresh } = useFetch<T>(url, options)

  watchEffect(() => {
    if (data.value) {
      sessionStorage.setItem(cacheKey, JSON.stringify(data.value))
      setTimeout(() => sessionStorage.removeItem(cacheKey), 5 * 60 * 1000)
    }
  })

  return { data, error, pending, refresh }
}

3. Pinia store с TypeScript и плагином persist

Для чего: Создать store для управления состоянием пользователя с сохранением в localStorage.

Промт:

Сгенерируй Pinia store 'useUserStore' для Nuxt 3 с TypeScript и persist (используй @pinia-plugin-persistedstate). Store должен содержать:
- state: user (null

| {id: number, name: string, email: string}), token (string | null), isAuthenticated (boolean)
- getters: isLoggedIn (возвращает true если token есть), userName (возвращает name или 'Гость')
- actions: login(email, password)  имитирует запрос к API, logout()  очищает state
- persist: только token и user
- Использовать defineStore

Пример:

export const useUserStore = defineStore('user', {
  state: () => ({
    user: null as { id: number; name: string; email: string } | null,
    token: null as string | null,
  }),
  getters: {
    isLoggedIn: (state) => !!state.token,
    userName: (state) => state.user?.name || 'Гость',
  },
  actions: {
    async login(email: string, password: string) {
      const { data } = await $fetch('/api/login', { method: 'POST', body: { email, password } })
      this.token = data.token
      this.user = data.user
    },
    logout() {
      this.token = null
      this.user = null
    },
  },
  persist: {
    pick: ['token', 'user'],
    storage: persistedState.localStorage,
  },
})

4. Middleware для защиты роутов

Для чего: Создать middleware, который проверяет аутентификацию и редиректит на страницу логина.

Промт:

Напиши Nuxt 3 middleware 'auth.ts', который:
- Проверяет useUserStore().isLoggedIn
- Если не авторизован, редиректит на /login с query-параметром redirect (текущий путь)
- Если авторизован и путь /login, редиректит на /dashboard
- Работает как на клиенте, так и на сервере (используй useRouter и useRoute)
- Добавь обработку ошибок (если store не инициализирован)

Пример:

export default defineNuxtRouteMiddleware((to) => {
  const userStore = useUserStore()

  if (process.server) {
    // На сервере store может быть пустым — пропускаем
    return
  }

  if (to.path === '/login' && userStore.isLoggedIn) {
    return navigateTo('/dashboard')
  }

  if (to.path !== '/login' && !userStore.isLoggedIn) {
    return navigateTo(`/login?redirect=${to.fullPath}`)
  }
})

5. Кастомный плагин для глобальных функций

Для чего: Создать плагин, который добавляет глобальный helper для форматирования дат.

Промт:

Создай Nuxt 3 плагин 'format-date.ts', который:
- Использует Intl.DateTimeFormat
- Добавляет в nuxtApp.provide функцию $formatDate(date, locale?, options?)
- По умолчанию locale = 'ru-RU', options = { day: 'numeric', month: 'long', year: 'numeric' }
- Обрабатывает null/undefined (возвращает '')
- Имеет автокомплит через расширение модуля (declare module '#app')

Пример:

export default defineNuxtPlugin(() => {
const formatDate = (date: Date

| string | null | undefined, locale = 'ru-RU', options?: Intl.DateTimeFormatOptions) => {
    if (!date) return ''
    const d = new Date(date)
    if (isNaN(d.getTime())) return ''
    return new Intl.DateTimeFormat(locale, options || { day: 'numeric', month: 'long', year: 'numeric' }).format(d)
  }

  return {
    provide: {
      formatDate
    }
  }
})

declare module '#app' {
  interface NuxtApp {
$formatDate(date: Date

| string | null | undefined, locale?: string, options?: Intl.DateTimeFormatOptions): string
  }
}

6. Кастомная директива v-focus

Для чего: Создать директиву, которая автоматически фокусирует элемент при монтировании.

Промт:

Напиши Vue 3 директиву v-focus, которая:
- При монтировании элемента устанавливает на него фокус
- Работает с input, textarea, select
- Если передан модификатор .delay, фокус устанавливается через 500ms
- Поддерживает аргумент: v-focus:delay="1000" для кастомной задержки
- Используй mounted и updated хуки
- Типизируй с помощью DirectiveBinding

Пример:

export const vFocus = {
  mounted(el: HTMLElement, binding: DirectiveBinding) {
    const delay = binding.arg === 'delay' ? (binding.value || 500) : 0
    setTimeout(() => el.focus(), delay)
  },
  updated(el: HTMLElement, binding: DirectiveBinding) {
    if (binding.value !== binding.oldValue) {
      const delay = binding.arg === 'delay' ? (binding.value || 500) : 0
      setTimeout(() => el.focus(), delay)
    }
  }
}

7. Композабл для Intersection Observer (ленивая загрузка)

Для чего: Создать composable для отслеживания видимости элемента (например, для lazy loading изображений).

Промт:

Создай composable useIntersectionObserver, который:
- Принимает ref на элемент
- Возвращает { isVisible, entry, observe, unobserve }
- Использует IntersectionObserver с options (root, rootMargin, threshold)
- Автоматически отключается после первого срабатывания (once: true)
- Работает в SSR безопасно (проверяет window)
- Написан на TypeScript

Пример:

export function useIntersectionObserver(
  target: Ref<HTMLElement | null>,
  options?: IntersectionObserverInit & { once?: boolean }
) {
  const isVisible = ref(false)
  const entry = ref<IntersectionObserverEntry | null>(null)

  let observer: IntersectionObserver | null = null

  const observe = () => {
    if (!window || !target.value) return

    observer = new IntersectionObserver(([e]) => {
      isVisible.value = e.isIntersecting
      entry.value = e
      if (options?.once && e.isIntersecting) {
        unobserve()
      }
    }, options)

    observer.observe(target.value)
  }

  const unobserve = () => {
    observer?.disconnect()
    observer = null
  }

  onMounted(observe)
  onUnmounted(unobserve)

  return { isVisible, entry, observe, unobserve }
}

8. Миграция Options API на Composition API

Для чего: Преобразовать старый компонент с Options API в современный Composition API.

Промт:

Перепиши следующий Vue 2 компонент на Vue 3 Composition API с TypeScript. Используй <script setup>. Сохрани всю функциональность:
- data: count (0), message ('hello')
- computed: doubleCount (count * 2)
- watch: count (логировать при изменении)
- methods: increment(), updateMessage(newMsg)
- mounted: вывести 'Component mounted'
- props: title (string, required)
- emits: 'update:count'

Исходный компонент:
export default {
  props: { title: String },
  data() { return { count: 0, message: 'hello' } },
  computed: { doubleCount() { return this.count * 2 } },
  watch: { count(val) { console.log(val) } },
  methods: { increment() { this.count++; this.$emit('update:count', this.count) }, updateMessage(msg) { this.message = msg } },
  mounted() { console.log('Component mounted') }
}

Пример результата:

<script setup lang="ts">
import { ref, computed, watch, onMounted } from 'vue'

const props = defineProps<{ title: string }>()
const emit = defineEmits<{ (e: 'update:count', value: number): void }>()

const count = ref(0)
const message = ref('hello')

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

watch(count, (val) => console.log(val))

function increment() {
  count.value++
  emit('update:count', count.value)
}

function updateMessage(msg: string) {
  message.value = msg
}

onMounted(() => console.log('Component mounted'))
</script>

9. Настройка динамических маршрутов в Nuxt

Для чего: Создать страницу с динамическими параметрами и middleware для проверки прав.

Промт:

Сгенерируй структуру страниц для Nuxt 3 с динамическими маршрутами:
- Страница /products/[id] с параметром id
- Страница /products/[id]/edit с middleware 'auth' и параметром id
- Внутри страниц используй useRoute() для получения id
- Добавь validate() для проверки, что id  число
- Компонент страницы должен быть типизирован

Пример для pages/products/[id].vue:

<script setup lang="ts">
const route = useRoute()
const id = computed(() => Number(route.params.id))

const { data: product, error } = await useFetch(`/api/products/${id.value}`)

if (error.value) {
  throw createError({ statusCode: 404, statusMessage: 'Product not found' })
}

definePageMeta({
  validate: (route) => {
    return !isNaN(Number(route.params.id))
  }
})
</script>

<template>
  <div>
    <h1>{{ product?.name }}</h1>
    <p>{{ product?.description }}</p>
  </div>
</template>

10. Кастомный Nuxt модуль

Для чего: Создать простой модуль, который добавляет глобальный компонент и композабл.

Промт:

Напиши минимальный Nuxt 3 модуль 'my-module', который:
- Регистрирует глобальный компонент 'MyButton' из ./runtime/components/MyButton.vue
- Добавляет композабл useMyHelper из ./runtime/composables/useMyHelper.ts
- Использует defineNuxtModule из 'nuxt/kit'
- Имеет опцию prefix (по умолчанию 'My')
- Версионирован (package.json с name и version)

Пример модуля:

import { defineNuxtModule, addComponent, addImportsDir } from '@nuxt/kit'
import { name, version } from '../package.json'

export default defineNuxtModule({
  meta: { name, version },
  defaults: { prefix: 'My' },
  setup(options) {
    addComponent({
      name: `${options.prefix}Button`,
      filePath: resolve(__dirname, './runtime/components/MyButton.vue')
    })

    addImportsDir(resolve(__dirname, './runtime/composables'))
  }
})

Эти промты сэкономят вам часы рутинной работы. Скопируйте нужный, вставьте в AI и получите готовый код. А если хотите углубиться — перечитайте официальную документацию Vue 3 и Nuxt 3: там много деталей, которые не влезли в промты.

← Все статьи

Комментарии

Читайте также

Конец эпохи GoPro: как Vibe Coding и AI меняют рынок экшн-камер

18 июля 2026

Как подключить 7-segment display (TM1637) к AI-агенту ASI Biont: автоматизация отображения данных без программирования

18 июля 2026

Матричное дифференцирование в машинном обучении: градиент, якобиан и линейная регрессия — разбор с примерами

18 июля 2026

Визуализация данных с AI-агентом: интеграция VGA output (ESP32 + DAC) и ASI Biont в реальном времени

18 июля 2026

Интеграция Industrial IoT Gateways с AI-агентом ASI Biont: предиктивное обслуживание и автоматизация без кода

18 июля 2026

Data Science для бизнеса: как AI-обучение на Asibiont решает проблему дефицита кадров в 2026 году

18 июля 2026

Интеграция OpenCart с AI-агентом ASI Biont: как автоматизировать управление заказами и товарами без программирования

18 июля 2026

Овладейте своим разумом: почему курс «Навыки обучения и техники запоминания» — лучшее вложение в вашу карьеру в 2026 году

18 июля 2026

Освоение российских норм охраны труда: почему курс Asibiont «Охрана труда и техника безопасности» — ваш быстрый путь к соблюдению требований

18 июля 2026