Перейти к содержимому

24. Nuxt vs Next.js

Оба фреймворка — лидеры SSR-экосистемы. Nuxt 3 построен на Vue 3, Next.js 15 — на React 19. Выбор зависит от команды, проекта и предпочтений. Давайте разберём детально.


  • Основан на: Vue 3 + Vite + Nitro
  • Язык: TypeScript (нативно)
  • Релиз: Ноябрь 2022
  • Сопровождает: Команда Nuxt Labs + сообщество
  • Философия: “Convention over Configuration”
  • Основан на: React 19 + Turbopack
  • Язык: TypeScript/JavaScript
  • Релиз: Октябрь 2024 (v15)
  • Сопровождает: Vercel
  • Философия: “App Router + React Server Components”

ХарактеристикаNuxt 3Next.js 15
ФреймворкVue 3React 19
РоутингФайловый (pages/)Файловый (app/)
SSR✅ Встроенный✅ Встроенный
SSGprerender: truegenerateStaticParams
ISRswr, isrrevalidate
Server Components🔄 В разработке✅ React Server Components
State ManagementPinia (встроено)Redux/Zustand (внешние)
МетаданныеuseHead(), useSeoMeta()generateMetadata()
Загрузка данныхuseFetch, useAsyncDatafetch() в Server Components
API Routesserver/api/app/api/
Middlewaremiddleware/middleware.ts
i18n@nuxtjs/i18n (официальный)next-intl, next-i18next
Изображения@nuxt/imagenext/image
ДеплойЛюбая платформа (Nitro)Оптимально на Vercel
DevToolsNuxt DevToolsReact DevTools

pages/
├── index.vue # /
├── about.vue # /about
├── blog/
│ ├── index.vue # /blog
│ └── [slug].vue # /blog/:slug
└── [...404].vue # 404
pages/blog/[slug].vue
<script setup lang="ts">
const route = useRoute()
const slug = route.params.slug
const { data: post } = await useFetch(`/api/posts/${slug}`)
</script>
app/
├── page.tsx # /
├── about/
│ └── page.tsx # /about
├── blog/
│ ├── page.tsx # /blog
│ └── [slug]/
│ └── page.tsx # /blog/:slug
└── not-found.tsx # 404
app/blog/[slug]/page.tsx
export default async function BlogPost({
params
}: {
params: { slug: string }
}) {
const post = await fetch(`/api/posts/${params.slug}`)
.then(r => r.json())
return <article>{post.title}</article>
}

<script setup lang="ts">
// useFetch — SSR + автокеш
const { data: users, pending } = await useFetch('/api/users')
// useAsyncData — кастомная логика
const { data: posts } = await useAsyncData('posts', async () => {
const posts = await $fetch('/api/posts')
return posts.filter(p => p.published)
})
// useLazyFetch — клиентская загрузка
const { data: comments } = useLazyFetch('/api/comments')
</script>
// Server Component (fetch с кешем)
async function UserList() {
const users = await fetch('/api/users', {
next: { revalidate: 3600 } // ISR 1 час
}).then(r => r.json())
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>
}
// Клиентский компонент
'use client'
function Comments() {
const [comments, setComments] = useState([])
useEffect(() => {
fetch('/api/comments').then(r => r.json()).then(setComments)
}, [])
return <div>{comments.map(c => <div key={c.id}>{c.text}</div>)}</div>
}

stores/user.ts
export const useUserStore = defineStore('user', () => {
const user = ref(null)
const isAuthenticated = computed(() => !!user.value)
async function login(credentials) {
user.value = await $fetch('/api/auth/login', {
method: 'POST',
body: credentials
})
}
return { user, isAuthenticated, login }
})
<script setup>
const userStore = useUserStore()
// Реактивно, SSR-совместимо из коробки
</script>
store/user.ts
import { create } from 'zustand'
const useUserStore = create((set) => ({
user: null,
isAuthenticated: false,
login: async (credentials) => {
const user = await fetch('/api/auth/login', {
method: 'POST',
body: JSON.stringify(credentials)
}).then(r => r.json())
set({ user, isAuthenticated: true })
}
}))

// Авто-импорт компонентов, composables, утилит
// Не нужно писать import!
const user = useAuth() // composable
const router = useRouter() // vue-router
// Авто-генерация TypeScript типов
// .nuxt/types/ создаётся автоматически
<!-- Компоненты используются без импорта -->
<template>
<BaseButton>Нажми</BaseButton>
<UserCard :user="user" />
</template>
// React Server Components — нет JS на клиенте
async function HeavyComponent() {
const data = await db.query('SELECT * FROM products')
// Этот код НЕ попадает в клиентский JS
return <div>{data.map(...)}</div>
}
// Параллельные маршруты
app/
├── @modal/
│ └── page.tsx # модальное окно
└── page.tsx # основной контент

  • Nitro — универсальный деплой на любую платформу
  • Vite — мгновенный HMR в разработке
  • Tree-shaking — автоматически
  • Route Rules — гибкое кеширование по маршруту
routeRules: {
'/': { prerender: true },
'/blog/**': { swr: 3600 },
'/api/**': { cors: true }
}
  • Turbopack — сборщик на Rust (быстрее Webpack)
  • React Server Components — минимальный JS на клиент
  • Partial Prerendering — смешанный SSR + Static
  • Edge Runtime — нативная поддержка
export const runtime = 'edge' // Edge Function
export const revalidate = 3600 // ISR

  • nuxt.com/modules — 200+ официальных модулей
  • Глубокая интеграция модулей
  • @nuxt/content, @nuxt/image, @nuxt/ui
  • Uno CSS, Tailwind, Nuxt UI Pro
  • npm — весь React-экосистем
  • shadcn/ui, Radix UI
  • Vercel Commerce, Vercel Analytics
  • next-auth, next-mdx-remote

Команда знает Vue или переходит с Nuxt 2 ✅ Нужна мощная i18n из коробки ✅ Файловая CMS с @nuxt/contentБыстрый старт с минимальной настройкой ✅ Деплой не на Vercel — Nitro даёт свободу

Команда знает ReactНужны React Server Components сегодня ✅ Деплой на Vercel с максимальной интеграцией ✅ Большая React-экосистема библиотек ✅ Корпоративный стандарт — React знают все