Next.js: Современные тенденции в веб-разработке (часть 1)
Актуальные требования веб-разработки предъявляют высокие стандарты к производительности и опыту пользователей. В данной статье мы рассмотрим Next.js
— инновационный фреймворк на языке JavaScript
, предоставляющий мощные средства для создания современных веб-приложений на базе React
, разработанный Vercel
. Узнаем, как Next.js облегчает создание веб-приложений с высокой производительностью, масштабируемостью и дружелюбностью к SEO. Мы также более подробно рассмотрим его функционал, включая серверный рендеринг и генерацию статических сайтов, предоставив примеры использования. Давайте разберем, почему Next.js становится важным инструментом в разработке веб-приложений, обеспечивая оптимальный баланс между функциональностью и производительностью.
Основные особенности Next.js
Next.js предлагает множество функций, которые привлекают разработчиков. Этот фреймворк, являющийся библиотекой для построения пользовательских интерфейсов, также может выполнять функции бэкенда благодаря наличию Node.js
и возможности переноса части логики на сторону сервера. Рассмотрим основные возможности Next.js.
- Серверный рендеринг (SSR) и генерация статических сайтов (SSG)
Next.js благодаря серверной прослойке на Node.js успешно осуществляет предварительный рендеринг страниц на стороне сервера, ускоряя загрузку и повышая SEO за счет передачи HTML-контента напрямую в браузер. Это улучшает опыт пользователя и видимость в поисковых системах. - Автоматическое разделение кода
Фреймворк автоматически разбивает код на управляемые блоки, что позволяет загружать только необходимые компоненты, уменьшая время первоначальной загрузки страницы и повышая ее эффективность. - Встроенная поддержка CSS
Next.js легко интегрируется с различными библиотеками CSS-in-JS, упрощая стилизацию и избавляя от необходимости использования дополнительных настроек и инструментов. - Горячая замена модулей (HMR)
HMR позволяет разработчикам видеть изменения в реальном времени, без необходимости обновления страницы, что улучшает процесс разработки. - Роутинг на основе файловой системы
Создание маршрутов в приложении Next.js не требует дополнительных усилий. Структура папок и файлов определяет доступ клиентской части к компонентам приложения. Кроме того, Next.js поддерживает ключевые слова в именах файловой системы для разделения поведения и контекста сервера и клиента. Например, компоненты внутри папки «api» рассматриваются как API-эндпоинты, а не компоненты, и не включаются автоматически в клиентскую часть приложения. Это облегчает интеграцию бэкенда с фронтендом, обеспечивая динамическую работу с данными.
Методики структурирования: Page роутер и App роутер
На старте Next.js использовал так называемую структуру построения приложения Page
, параллельно работая над более интуитивно понятной структурой App
. В марте 2023 App
роутер официально вышел из бета-версии. Давайте подробнее рассмотрим основные принципы каждой из этих структур.
Page роутер
Это устоявшаяся и проверенная временем парадигма в Next.js.
- Файловая структура роутинга
Page структура создает роутинг на основе файловой системы приложения. Основной точкой входа является папка с особым именем "pages"
, внутри которой каждый файл с расширением .jsx
или .tsx
представляет собой маршрут в навигации приложения. Например, файл с именем about.tsx
в папке pages
формирует маршрут /about
в вашем приложении. Также можно создавать дополнительные папки внутри pages для группировки компонентов по их роли и направлению. Например, файл по пути pages/help/contact-us.tsx
предоставит доступ к компоненту по адресу /help/contact-us
. Этот подход к организации роутов упрощает работу разработчиков, так как не требуется ручное создание и прописывание роутинга в компонентах, что также улучшает читаемость кода.
- Динамический роутинг
Next.js поддерживает динамическую маршрутизацию с использованием скобок []
в именах файлов. Например, файл с именем [id].tsx
в папке user
может соответствовать маршрутам /user/1
, /user/2
и так далее. Динамические маршруты позволяют создавать адаптивные и параметризованные веб-адреса, предоставляя пользователям индивидуальный контент.
Полную систему роутинга можно описать следующей схемой:
- Зарезервированные имена
Для упрощения процесса разработки приложения, помимо роутинга, основывающегося на структуре файлов, Page
роутер взаимодействует с компонентами, имеющими зарезервированные имена, предназначенные для автоматической поддержки различных сценариев. Одним из ключевых зарезервированных компонентов является _app.jsx/tsx
– корневой компонент, расположенный непосредственно в папке pages
. Как и в стандартных React
-приложениях, его роль заключается в обеспечении инициализации общих макетов (UI-элементов, таких как заголовок или подвал веб-сайта) и любой глобальной логики, такой как React-контексты или управление глобальным состоянием. Еще одним широко используемым зарезервированным именем является index
. Компоненты с этим именем отображаются приложением на базовых маршрутах, то есть pages/index.tsx
доступен по адресу /
, а index
-компонент внутри каждой подпапки pages
обрабатывается на базовом маршруте соответствующего адреса.
Также существует несколько других зарезервированных имен (файлы должны непосредственно находиться в папке pages
):
_document.jsx/tsx
– малоиспользуемый компонент, предназначенный для переопределения стандартной HTML-структуры документа Next.js;404.jsx/tsx
– компонент для настройки страницы ошибки 404 сервера Next.js (например, при попытке доступа по несуществующему маршруту в URL). Если пользовательский компонент отсутствует, Next.js статически создаст стандартную страницу ошибки во время сборки приложения;500.jsx/tsx
– аналогично для ошибки 500;_error.jsx/tsx
– компонент для настройки обработки серверных ошибок, заменяющий стандартные компоненты 404/500.
- Автоматическое разделение кода
Next.js автоматически осуществляет разделение кода, загружая только необходимые фрагменты JavaScript для текущей страницы. Это сокращает время загрузки страницы и повышает общую производительность приложения. Автоматическое разделение кода гарантирует, что пользователи получают оптимизированные и легкие блоки кода, специфичные для их конкретных взаимодействий.
- Получение данных
В Page роутере реализованы методы получения данных, такие как getStaticProps
и getServerSideProps
. Эти методы позволяют получать данные во время рендеринга на стороне сервера или во время сборки, что обеспечивает предварительный рендеринг страниц с динамическим содержимым. Это гарантирует, что веб-приложение будет предоставлять пользователям актуальные данные без ущерба для производительности. Рассмотрим их подробнее.
getStaticProps
– метод, указывающий компоненту Next.js выполнить внутреннюю логику и передать полученные данные (пропы) для прорисовки во время процесса сборки приложения. Рендеринг на этапе сборки с использованием getStaticProps()
означает, что Next.js конвертирует React-код в стандартные HTML-страницы перед размещением компонента. Это оптимально для SEO
и обеспечивает быструю загрузку страниц. Этот метод называется статической генерацией сайтов, или SSG
.
import type { InferGetStaticPropsType, GetStaticProps } from "next"
type Product = {
id: number
name: string
price: number
isAvailable: boolean
};
export const getStaticProps = (async (context) => {
const res = await fetch("https://...")
const product = await res.json()
if (!product) {
return {
notFound: true
}
} else if (!product.isAvailable) {
return {
redirect: {
destination: "/products",
permanent: false,
}
}
}
return { props: { product } }
}) satisfies GetStaticProps<{ product: Product }>
export default function Page({ product }: InferGetStaticPropsType<typeof getStaticProps>) {
return product.name
}
Метод getStaticProps()
представляет собой асинхронную функцию с доступом к объекту context
, который может содержать параметры динамического роута, локализацию и другие. У этого метода есть три варианта возвращаемых значений:
notFound
– осуществляет переадресацию на страницу 404. В данном случае, если попытаться получить информацию о каком-либо товаре, этот сценарий будет активирован, еслиAPI
вернет пустые данные.redirect
– изменяет текущий маршрут на указанный. Например, в случае нахождения продукта, но параметрisAvailable: false
указывает на его отсутствие в наличии, произойдет переход на страницу продуктов.props
– возвращает непосредственно данные, необходимые для последующего рендеринга компонента.
Метод getServerSideProps
— это инструкция для компонента Next.js, чтобы выполнить внутреннюю логику и передать полученные данные (пропы) для последующего рендеринга во время так называемого рантайма. В отличие от статической генерации через getStaticProps()
, где HTML создается один раз и сохраняется для последующих запросов, getServerSideProps()
предполагает создание серверной прослойки для каждого запроса страницы. В таком случае логика внутри метода, передача пропов и частичный рендеринг компонента выполняются на стороне сервера для каждого клиентского запроса. После обработки на сервере, клиент получает базовый неинтерактивный HTML, а также получает JSON-объект пропов и JavaScript-инструкции для добавления интерактивности компоненту. Затем происходит процесс «гидрации» – выполнение минимального набора инструкций на клиентской стороне для получения полноценного интерактивного компонента.
import type { InferGetServerSidePropsType, GetServerSideProps } from "next"
type Product = {
id: number
name: string
price: number
isAvailable: boolean
};
export const getServerSideProps = (async (context) => {
const res = await fetch("https://...")
const product = await res.json()
return { props: { product } }
}) satisfies GetServerSideProps<{ product: Product }>
export default function Page({ product }: InferGetServerSidePropsType<typeof getServerSideProps>) {
return product.name
}
Как видно, способ использования мало отличается от getStaticProps()
, однако важно четко определять случаи применения каждого метода. В основном, компоненты с getStaticProps()
рендерятся только один раз во время сборки приложения и подходят для улучшения производительности и SEO, если они зависят от редко изменяемых внешних данных. Примером может служить страница блога, где информация редко меняется в базе данных. В случае страницы продукта, более подходящим будет использование getServerSideProps()
, так как информация о товаре (цена, наличие и т. д.) вероятно будет часто изменяться и должна быть всегда актуальной. Получение обновленных данных для каждого запроса страницы обеспечивается максимальным выполнением логики рендеринга на стороне сервера (SSR
). Хотя это немного уступает в скорости получения контента и SEO по сравнению со статической генерацией, но все равно значительно лучше, чем выполнение всего процесса на стороне клиента, как в стандартных SPA
, написанных на React
или других фронтенд-фреймворках.
- API Routes
В силу наличия сервера Node.js
в Next.js, как мы ранее выяснили, Page роутер обеспечивает удобное создание API-эндпоинтов в вашем приложении. Поместив файлы в папку pages/api
, можно определить серверные функции, обрабатывающие HTTP-запросы. Вызов эндпоинтов также подчиняется структуре файловой системы, где адрес формируется на основе имен папок и файлов с логикой API. Эта логика существует исключительно на стороне сервера и не увеличит размер клиентского приложения. Эта возможность упрощает разработку бэкенда и обеспечивает беспрепятственное взаимодействие между клиентом и сервером, позволяя создавать интерактивные веб-приложения. В некотором смысле, это можно сравнить с MERN-стеком
, где бэкенд также написан на JavaScript
для упрощения коммуникации.
import type { NextApiRequest, NextApiResponse } from 'next'
type ResponseData = {
message: string
}
export default function handler( req: NextApiRequest, res: NextApiResponse<ResponseData> ) {
res.status(200).json({ message: 'Привет всем!' })
}
Эндпоинт вернет JSON-объект со статусом 200.
В следующей части расскажем про
App роутер