📱

Get Our Mobile App

Take your business learning on the go!

Download on the App StoreGet it on Google Play

Быстрый курс по Next JS за 2 часа для начинающих

ВебКадеми | Юрий Ключевский1:58:40

Transcription

Всем привет, друзья. На связи Юрий Ключевский веб-академии. И в этом видео мы рассмотрим основы работы с фреймворком Next.js. Познакомимся с ним, установим и соберём на нём один несложный сайт, который будет выглядеть следующим образом. У нас будет главная страница, а другие страницы на сайте. Мы научимся получать данные с других сервисов по API, выводить их, делать страницы с маршрутами по параметрам и даже создадим своё собственное API внутри Next, к которому будем делать запросы, обращаться и выводить результаты. А по ходу, конечно же, рассмотрим разницу между серверными и клиентскими компонентами. Поработаем с компонентом Image, который есть в Next.js для оптимизации графики. А, поработаем с метаданными. И в конце всё это задеплоим на Vercel. Поэтому присоединяйтесь к просмотру, будет интересно и полезно.

[музыка]

Итак, мы начинаем. Не забудьте поставить лайк этому видео, подписаться на канал, чтобы не потерять его, и также в будущем получать от меня уроки по frontend разработке.

Итак, начнём с того, что создадим приложение на Next.js. Для этого используется команда `npx create-next-app@latest`. А также это вы можете всегда подсмотреть в документации по Next.js на официальном сайте nextjs.org. Всегда можно зайти в docs, здесь замечательная документация, и перейти далее слева в раздел installation. И, собственно, вот он пример установки с использованием пакетного менеджера npm: `npx create-next-app`.

Для прохождения этого урока я подразумеваю, что вы уже знакомы с JavaScript, React, Node.js, npm, это всё не вызывает вопросов, да? И вот вы писали проекты на React или познакомились с ним по нашим урокам. К слову, у меня есть очень классные уроки по React на канале. Ссылочка также будет в описании. И вот после React решили познакомиться с Next.js. Вот именно этим мы сегодня и будем заниматься, да. Поэтому, собственно, установить приложение с Next.js, развернуть проект можно вот такой командой. А, собственно, что мы и сделаем.

То есть я перехожу на рабочий стол. Далее я открываю терминал. В терминале перемещаюсь на рабочий стол. В моём случае это `cd desktop`. И далее ввожу сюда команду `npx create-next-app@latest`. Собственно, это можно всё дело скопировать, например, вот так: `npx create-next-app@latest`. Далее через пробел идёт название папки, в которой это всё будет создано. Давайте назовём `next-tutorial`. А пробел и два дефиса — это для того, чтобы установить все зависимости и настройки со значениями по умолчанию. Я же предлагаю два дефиса `yes`. Этот флаг не вводить, и мы с вами вручную укажем, что мы будем добавлять в проект, а что нет. Потому что Next.js как бы на старте по умолчанию он идёт и с TypeScript, и с Tailwind, и с линтером. Есть даже два варианта для линтинга. И на старте я рекомендую вообще завести просто чистый Next.js на обычном JS. Ну, то есть для того, чтобы вы чётко поняли, что есть нового в Next.js по сравнению с React. Только на этом сфокусировали своё внимание, а уже потом, да, уже потом вы добавите туда и TypeScript, и Tailwind для стилей, и так далее, и так далее. Вот.

Поэтому вводим просто команду `npx create-next-app@latest` и название папки, в которой это будет создано, `next-tutorial` в моём случае. Нажимаю Enter.

После этого он начинает меня спрашивать, хочу ли я использовать TypeScript. Я выбираю нет. Далее, какой линтер мы будем использовать, тоже выберем. Мы не будем использовать линтер. Используем ESLint, тоже указываю нет. Далее, а хотим ли мы разместить код внутри директории `src`? Ну, в принципе, это удобно, а, для того, чтобы не размещать прямо в корне проекта. Поэтому выбираем `yes`. Далее, хотим ли мы использовать App Router, рекомендуемо. И выбираем `yes`. То есть это, ну, как бы самый последний, самый новый роутер, который был в Next.js, который основан на директориях, на именах папок, файлов. Собственно, мы его будем сегодня рассматривать, поэтому, конечно, `yes`. А далее, хотим ли мы использовать Turbopack для сборки? Рекомендуется `yes`. И далее, хотим ли мы кастомизировать import aliases, ярлыки для импорта одних файлов в другие? Это удобно. Выбираем `yes`. Мы это тоже посмотрим. И далее он нам предлагает настроить их. Ну, мы их потом настроим. Потом это проще сделать, как по мне, в редакторе. Я тут просто нажму Enter.

Отлично. Проект был создан. На рабочем столе у меня была создана вот такая папка `next-tutorial`. Внутри неё уже установлены все зависимости. Давайте закинем её в редактор кода. В моём случае это Visual Studio Code, и сделаем обзор файлов стартового проекта. Давайте его также запустим, посмотрим, что он работает, и сделаем обзор файлов.

Открываю терминал. Открыть его можно через `View` -> `Terminal` или горячие клавиши. И ввожу здесь команду `npm run dev` для того, чтобы запустить наш проект. Собственно, в консоли он мне пишет, что проект запущен на адресе `localhost:3000`. Зажимаю Command или Ctrl+Windows, кликаю по этому адресу, и вот во вкладке в браузере у меня открывается проект. Отлично.

А чтобы убедиться, что это действительно тот проект, который открылся, если вы в этом хотите, то вы можете отредактировать какие-либо файлы. То есть, например, можно зайти в `src` -> `app`, зайти в `page.js`. То есть `page.js` — это, по сути, вот эта вот страница, которую мы здесь видим, и внести сюда какие-нибудь изменения. Например, давайте вот перед списком `ol` добавим сюда тег `p` и напишем "Hello Next.js", например. Вот так. Переходим, смотрим и видим "Hello Next.js".

Теперь я сделаю обзор файлов и директорий в стартовом проекте Next.js. Итак, давайте перейдём в редактор. Что мы видим? Значит, у нас есть несколько папок и файлов. Начнём с файлов. `.gitignore`. Ну, в принципе, для системы контроля версии Git, то есть файлы, которые не попадают в Git. `jsconfig.json` — настройка сборки. Далее `package-lock.json`, `package.json`. В общем, зависимости и зависимости зависимостей, да. Все скрипты, которые мы вот запускаем, `npm run dev`, `build`, `start`, какие есть на старте, они, естественно, записаны в `package.json`. Вы всегда их можете здесь посмотреть в разделе `scripts`. Далее `README.md` с описанием. `next.config.js`, и, [музыка] а, собственно, директории.

В директориях у нас есть директория `.next`, в которой собираются кэш-файлы работы нашего приложения после того, как мы его запускаем. Короче, эту папку мы не трогаем, руками сюда не лезем, и она генерируется автоматически, изменяется, наполняется тоже автоматически. То есть, ну, во время запуска билда сюда складывает какие-то свои, а, файлы, которые нужны для ускорения работы приложения во время его разработки. Далее, `node_modules` — ну, собственно, все зависимости Next.js и так далее.

В папке `public` находятся статические файлы, которые, а, как бы, да, будут потом размещены на сайте, которые мы можем использовать в нашем приложении. То есть, чтобы не размещать их в папке с кодом, мы их можем вынести в папку `public`. И использовать её мы можем просто по слэшу внутри нашего приложения. То есть можем просто писать там `/` какая-то картинка, и это будет подразумеваться, что мы уже обращаемся в папку `public`. Ну, мы дальше это посмотрим.

В директории `src` находится пока вот одна директория `app`, то есть это наше приложение. Здесь же в `src` мы можем потом создавать и другие папки для того, чтобы описывать структуру приложения. Там папку для компонентов, для типов, для каких-то утилит, для каких-то helper функций и так далее. То есть вот всё приложение внутри папки `src` находится, а внутри папки `app` происходит роутинг по страницам нашего приложения. То есть по умолчанию главная страница — это страница `page.js`, которая находится внутри папки `app`. То есть вот когда мы заходим `localhost:3000/` — вот просто в корень, соответственно, открывается страница из файла `page` в папке `app`. Вот.

Теперь разберём файлы, которые здесь находятся внутри. А, значит, `page.js` — это главная страница, и она наследует `layout.js`. То есть `layout.js` — это общий шаблон страницы. То есть давайте просмотрим этот файл `layout.js`. Ну, здесь вначале подключены шрифты, стили. Со шрифтами мы ещё чуть позже немножко поработаем. Потом тут метаданные, мы тоже их далее рассмотрим. И `layout.js`, он экспортирует вот такой компонент `Layout`, который отрисовывает `html`, `body` и далее `children`, то есть дочерние компоненты. И вот `layout.js`, он является как бы общим таким шаблоном для вообще всех страниц сайта. То есть вот он рендерит вот эту разметку, и все страницы они вот здесь вот внутри него в качестве `children` потомков будут отрисовываться. То есть наша `page.js`, она отрисовывается внутри `layout.js`. Вот. Ну, а `page.js` — это, собственно, компонент такой же, как обычный React-овский компонент, который возвращает JSX разметку, вот, который будет отрисован внутри `layout.js`, то есть вот `div`, картинки, там какая-то разметка и так далее. Вот. Ну, далее мы со всем этим будем работать. Сейчас ещё будем свои страницы создавать, поэтому всё это мы пощупаем.

Далее тут `favicon.ico` у нас находится и два файла со стилями: `global.css` и `page.module.css`. То есть `global.css` у нас подключен в `layout.js`, вот он вверху, а `page.module.css` у нас подключен уже непосредственно на странице. А далее я говорил, что в папке `public` мы складываем статические там файлы, картинки, которые просто хотим использовать на сайте. И вот, например, в `page.js` мы можем посмотреть, что у нас есть компонент `Image`, в котором мы отображаем в данном случае логотип Next.js. Вот `next.svg`, то есть это вот вот эта вот картинка Next.js. Она у нас лежит в папке `public` и в `src` мы просто указываем `/nextjs`, то есть как бы корень уже подразумевается, что это будет папка `public`. И вот, да, там мы можем складывать различные статические файлы.

Собственно, вот так вот выглядит стартовый проект, с которого мы начнём. Мы его, естественно, почистим, немножко переделаем под себя и создадим на нём сайт, такой базовый простой сайт, в процессе создания которого вы и познакомитесь с Next.js.

Далее я немного настрою проект под удобное форматирование, с которыми я привык работать. Ну, собственно, это не обязательно, но если вы хотите, вы также сможете это повторить. Все файлы, которые я буду использовать или настройки, будут описаны на отдельной странице, ссылка на которую будет в описании под видео. Вот. Значит, первое, что я сделаю — это добавлю следующий файл `.editorconfig` в редактор, который просто описывает форматирование. То есть в моём случае, что для меня важно, он говорит, что отступы будут сделаны с помощью табов, и один таб равняется четырём пробелам. Вот. Также я докину сюда ещё вот такой файлик `.prettierrc.json`, который, собственно, ну, говорит то же самое, в общем, тоже задаёт настройки для форматирования. И внутри Visual Studio Code у меня установлены плагины Prettier и EditorConfig. Вот поэтому, а, как бы, да, если кому интересно, то есть вы можете тоже их поставить, если у вас их ещё нет, Prettier настроить и использовать. То есть есть вот такой Prettier и есть EditorConfig. Вот он, собственно, он и нужен для того, чтобы VS Code хорошо, корректно работал с файлом настройки EditorConfig. Вот. Поэтому вот такие вот настроечки я добавляю. И теперь у меня должно измениться следующее. То есть я, например, открываю `layout.js`, нажимаю `Command+D`. И вот мы видим, то есть он у меня уже отформатировался так, как мне удобно. То есть отступы с помощью табов, один таб, четыре пробела. То есть открываю другую страницу, `Command+S`. Всё. И мне теперь с этим удобно и приятно работать.

И пока разрабатываем, хочу сказать, что весь код с данного урока, стартовый и финальный, вы сможете скачать в моём Telegram-канале. Ссылка на него в описании под видео.

А теперь очистим наш проект от стартового кода разметки и стилизуем его и начнём делать наш сайт.

Итак, сейчас проект запущен. Если открыть терминал, вот он у меня сработает. И он работает на `localhost:3000`. То есть мы видим вот такую страничку. Давайте сейчас всё вот это аккуратненько почистим. И я добавлю сюда свои стили и свою разметку.

Значит, первое, что я сделаю, я зайду в `page.js` и вообще удалю всё, что здесь есть в `page.js`. То есть вот так вот сверну тег, сверну футер. Вот. И просто напишу здесь, например, `<h1>Page JS</h1>`, то есть вот так просто вместо всего, что здесь было, и удалю импорты, которые были вверху. То есть `page.js` оставлю вот таким абсолютно пустым. Далее `page.module.css` я удалю. Все стили у нас будут в `global.css`. Далее `layout.js`, вот, который рендерит нашу страничку. В принципе, оставляем так, как есть. По поводу шрифтов мы дальше ещё посмотрим и удалим те, которые не нужны. Посмотрим чуть более детально, как они работают. Вот. И `global.css` я тоже поменяю на свой файлик, который я уже подготовил. Значит, этот файлик и его код вы сможете взять опять же по ссылке в описании. То есть вот так. Это примерно будет выглядеть. Зайдёте на блог web-academy и здесь будет вот такой вот код файла со стилями, которые я использую. Вы его просто вот так возьмёте, скопируете и вставите в `global.css` вместо того, что здесь есть. Вот таким образом.

Ну, перейдём теперь на страницу с сайтом. А видим, что, да, он сейчас вот так вот. Сейчас он у меня ругается. То есть ругается он на какую-то страницу. Вот он показывает ошибку. Если кликать где-то за её пределами, она пропадёт. Но вот внизу в левом нижнем углу вы можете нажать и увидеть, то есть что за ошибка. То есть в `page.js` применяется класс для стилей `styles-page`, а подключение этих стилей я удалил. Поэтому вот этот `div classname="styles-page"` тоже надо удалить. То есть оставим просто `div` тег `<h1>Page JS</h1>`. Вот таким образом. Переходим, проверяем. Отлично. То есть у нас показывается главное `Page JS`. Стили уже применились наши, никаких ошибок нет, всё здорово.

Значит, теперь следующее, что мы сделаем — это, ну, давайте начнём разрабатывать, значит, наш проект и добавим сюда немножко компонентов, навигацию, подвал и содержимое для страницы.

Итак, содержимое для `page.js`, то есть то, что отображается на главной странице. Я его сделаю следующим. То есть добавлю сюда заголовок "Изучаем Next.js", добавлю какое-то небольшое описание, чтобы у нас было вот. Далее помещу это всё не в `div`, а в `section` с классом `classname` и напишем здесь `content`. Посмотрим, как это будет выглядеть. И вот такая у нас картинка.

Теперь поработаем с `layout.js`. То есть `layout.js` — это, ну, собственно, вот наша обёртка, которая используется для всех страниц. Заходим в `layout.js`. И здесь в `body` я удалю вообще все `classnames`, которые здесь присутствуют. Они нам не понадобятся. И далее для красивых стилей, для красивого фона добавлю `div` с классом `BG-grid`. Вот так. `div classname="BG-grid"`. Просто он будет лежать вверху. Далее сделаю тег `main`. Внутрь него помещу вывод `children`. И для тега `main` дам следующие стили. `classname` у меня будет `container`, `container`, пробел и `page-padding`. Перейдём, посмотрим, как это выглядит. Вот так. То есть появился симпатичный фон в виде точек. И вот так выглядит теперь главная страница. Отлично.

Теперь давайте создадим компоненты. Компонент с навигацией и с подвалом, которые будут присутствовать на всех страницах, и определим их в `layout.js`. Значит, для этого возвращаемся в редактор. И здесь я ещё сделаю такую небольшую правку. То есть, в принципе, так как я не использую TypeScript, поэтому у меня файлы здесь JavaScript-овые `.js`, да, но я бы переименовал их в `.jsx`. Мне так более привычнее. То есть тот файл, который является компонентом в React или в нашем случае в Next.js и который возвращает JSX разметку, переименовывать его в `.jsx`. То есть как бы наглядно понятно, что это файл компонента или страница, да? А тот файл, который просто `.js` и просто имеет набор каких-то функций или выполняет какую-то работу, но не возвращает компоненты, тот просто `.js`. О'кей.

А давайте создадим компоненты. Значит, компоненты мы создаём, а, ну, опять же, их можно разместить-то, в принципе, да, там, где угодно, но а я создам отдельную папку в папке `src`, вот, и назову её `components`. Здесь у нас будут находиться компоненты. И у компонентов также, как в React, вы можете делать отдельную папку для каждого компонента, если там у него несколько файлов, стилей и так далее. Вот в нашем же случае они будут довольно простые. Все стили у нас в `global.css`, поэтому я просто буду создавать компоненты сразу здесь. И давайте создадим сразу простой компонент `Footer.jsx`, где у нас будет, собственно, подвал сайта. Ну, здесь мы описываем просто обычный компонент, как это и в React описывается. То есть функция, в данном случае стрелочная, которая возвращает вот такую JSX разметку. Футер как бы копирайт веб-академии и экспортирует его. Вот.

Теперь давайте его использовать. Я его подключу сразу в `layout.js`, то есть, чтобы он был на всех страницах сайта и мне не нужно было его там дублировать. Вот. Поэтому вот здесь вот под тегом `main` я сразу подключу `Footer`. Он у меня уже открыт, поэтому я начинаю набирать его вот так: `Footer` и он мне подсказывает `Footer`. Нажимаю Enter. Он его автоматически вот импортировал наверх. Пробел `/` закрываю. Отлично. Переходим. Проверяем. Вот подвал "Web-academy" у нас присутствует.

Теперь давайте сделаем компонент для навигации, то есть наши ссылочки, и сразу создадим и другие страницы. Да, наверное, логичнее создать, начать с создания других страниц, потом сделать для них навигацию. Значит, в рамках роутера Next.js, как я уже сказал, роутер [музыка] производит навигацию внутри папки `app`. То есть `page.jsx` — это наша вот главная страница. А теперь, если я хочу создать ещё какие-то дополнительные страницы, я создаю здесь директории. То есть внутри папки `app`, давайте закроем всё лишнее, я создаю директорию `about` и внутри `about` создаю `page.jsx`. И здесь я создаю какой-то компонент, который будет возвращать JSX разметку этой, собственно, и будет страницей `about`. Вот.

Поэтому давайте напишем здесь. Я напишу `SFC`, то есть stateless functional component в React обычный вот стрелочный компонент. Назовём его `AboutPage`. А, собственно, вот этот компонент, ну, вот эту функцию, да, её можно назвать как угодно, потому что она экспортируется по дефолту, собственно, как и вот в `page.js` этот `home` и в `layout.js`, вот `function RootLayout`, потому что они экспортируются по дефолту и, в принципе, они будут использоваться, да, а как нужно. Поэтому называть этот компонент в `about` могу как угодно. Ну, естественно, лучше с заглавной буквы, то есть по конвенции, именования компонентов. Ну и далее здесь какую-то разметку, которую он будет возвращать. То есть, например, `section` с классом `content`. У меня это будет не `selection`, а `section`. Вот так. `section` с классом `content`. А далее давайте добавим сюда заголовок. Добавим сюда `h2` с классом `section-title` с названием "About" и дадим сюда какое-то описание, чтобы был текст, опять же, какой-то абзац про Next.js. Вот. И таким образом у нас появилась страничка `about`. То есть мы можем на неё зайти. Давайте перейдём `localhost:3000/about` enter. И вот теперь по адресу `about` мы видим страницу `about`. То есть вот таким образом это работает.

Ну да, если ещё потренироваться, вы можете самостоятельно попробовать создать страницу для контактов. Давайте я это тоже проделаю. То есть `contacts`, чтобы наш сайт не был совсем пустым. Давайте создадим в папке `app` новую папку `contacts` и внутри неё создаём `page.jsx`. Далее здесь `SFC`. Также называем, в принципе, как удобно. Например, `ContactsPage`. Я назову `ContactsPage`. И оно мне будет возвращать следующую разметку. Собственно, `section` с классом `content`, заголовок и тег `p` с какими-то контактными данными. Вот. И теперь по адресу нашего приложения `/contacts` у нас откроются контакты. Давайте перейдём, проверим. Вот так. `/contacts` enter. И мы наблюдаем страницу "Contact". Отлично.

А теперь для того, чтобы сделать ссылки на эти страницы, мы можем использовать компонент `Link`, который есть в Next.js. То есть как вы видите, в Next.js нам не нужно подключать какой-то дополнительный роутер, типа `react-router-dom` и так далее. То есть Next.js уже имеет этот функционал встроенным. Вот. Ну и для того, чтобы сделать навигацию, я опишу её в отдельном компоненте и подключу её в `layout.js`, чтобы она также была на всех страницах сайта. Поэтому давайте так. В `components` создадим новый компонент `Navigation.jsx`. А пока просто сделаем здесь `SFC` и назовём его как `Navigation` и сделаем `return`. Будет возвращать тег `nav` с классом `navigation`. Не навигатор, а тег `nav` с классом `Navigation`. И давайте пока просто напишу здесь "Navigation" и выведу его на странице. То есть мы увидим, что он уже будет отображаться. То есть я его описал в компонентах. Далее перехожу в `layout.js` и после `div` с `BG-grid` перед `main` начинаю его писать `Navigation`. Вот мне подсказывает из `components/Navigation` tab enter пробел `/` закрываю. Собственно, вот `Navigation` компонент был подключен. Отлично. Переходим и, ну, например, на главную. В принципе, мы уже видим наш блок с навигацией. Вот он вверху отобразился. Отлично.

Значит, как прописываются ссылки? Они прописываются с помощью компонента `Link`, который берётся из `next/link`. И сейчас мы это сделаем. То есть вот здесь в компоненте `Navigation` внутри тега `nav` я начну прописывать компонент `Link`. И вот он мне подсказывает `Link` взять из `next/link`. Здесь я нажимаю Enter, он мне добавляет импорт. Вот пробел `/` закрываю. Хотя нет, наверное, немножко поспешил. То есть `Link` сделаю вот так. Открывающий и закрывающий. Вот внутри я пишу, собственно, текст ссылки `Main`. А далее внутри первого открывающего тега `Link` я указываю `href`, куда он будет ссылаться. То есть `href` и просто `/`, то есть он будет у меня ссылаться на главную страницу. Ну и также я добавлю к нему `classname`, то есть стилизую его. Вот так. Это будет `nav-link`. Отлично. А далее я продублирую его несколько раз, так как у нас есть ещё страница помимо `Main`, то есть у нас есть страница `About`. И пропишем для неё ссылку `/about`. И есть страница контакты `contacts`. И пропишем также ссылку `/contacts`. Собственно, вот они, три ссылочки есть в навигации описаны. Переходим, проверяем. Отлично. И теперь мы можем кликать и перемещаться между страницами по нашему сайту.

Далее давайте рассмотрим алиасы. Они уже по умолчанию прописаны, их можно использовать в Next.js. Когда мы создавали приложение, там также был вопрос: прописать ли базовые стартовые алиасы. Я закрою все страницы и открою `layout.js`, так как сюда я уже подключал компонент `Footer` и `Navigation`, и вы можете заметить, что пути к ним прописаны вот следующим образом. То есть, в принципе, здесь можно было бы прописать путь вот так. То есть там, например, текущая директория — это `app`, да? Дальше выйти на уровень вверх, зайти в `components`, подключить `Footer`, да, и, ну, с навигацией можно было бы поступить также. Он уже начал использовать алиасы. И вот в данном случае сейчас есть только один алиас, пока прописанный. Это символ `@`, который ссылается на корневую директорию, то есть на на директорию `src`. Вот. И далее уже из `src` он заходит в `components` и выбирает навигацию. Алиасы у нас прописаны, ну, в данном случае, так как у меня нет TypeScript, в `jsconfig.json`. Если бы был бы TypeScript, то мы бы смотрели `tsconfig.json`. Вот идём сюда. И, собственно, вот они `paths`. Вот он алиас стартовый. Давайте добавим сюда ещё алиас для компонентов. То есть я эту строчку продублирую, ставим запятую после неё. И далее укажем, что у нас будет вот так: алиас `components/*` будет ссылаться на `src/components/*`. Вот. И теперь для того, чтобы подключать компоненты, и неважно где, в каком файле я буду находиться, например, в `layout.jsx` или где-то внутри какой-то страницы, мне нужно будет подключить файл из компонентов. Мне не нужно будет думать, сколько прописать выходов уровней наверх. То есть я просто буду писать `@/components`. То есть давайте сделаем навигацию таким образом, подключим и подвал подключим таким же образом. Вот просто `@/components`. И далее внутри папки `components` `Footer` и `Navigation`. Перейдём и обновим страничку. Вот смотрим, видим, всё это отлично продолжает работать. Никаких ошибок у нас нет, всё корректно. И вот так вот просто можно использовать алиасы.

Далее разберёмся с шрифтами и с работой лейаутов.

Итак, я перемещаюсь в `app/layout.jsx`. А здесь со стартового шаблона у нас осталось два подключенных шрифта. Значит, если вам нужно подключить шрифты, которые находятся в Google Fonts, то вы можете это сделать вот таким образом. Просто импортировать их из пакета `next/font/google`, как это сделано вот с двумя шрифтами, которые у нас описаны ниже. Сейчас же вместо этих шрифтов я подключу другой. Мы его пропишем в CSS и посмотрим, как это работает для наглядности. Итак, я удалю два вот этих объекта, которые описывают эти шрифты, потому что они у

нас никак не задействованы, вот эти переменные, ну, эти константы, поэтому я их удаляю.

Далее вверху в импорте вместо этих двух шрифтов я подключу шрифт enter, то есть импорт enter from next font Google. Далее опишу данный шрифт, и будет это выглядеть следующим образом.

Создаю константу, в которую вот так определяю enter. И, значит, здесь у нас будет такая функция от вот этого шрифта. Даю объект, то есть те начертания, которые хочу подключить, сапсеты, кириллицу и латиницу. Далее swap для отображения и создать переменную в CSS, которая будет называться как font inter.

И теперь, чтобы вот эту переменную в CSS использовать, то есть, чтобы она там была и она там существовала, её нужно добавить. И добавляю её вот сюда к HTML. Прописываю класс name равно фигурные скобки. И далее ent тоable. Вот таким образом. То есть здесь, потому что вот мы создали константу. Вот. Теперь переменная font inter доступна в CSS. То есть я могу зайти в Global CSS и, например, вот в теге Body, давайте здесь внизу укажу font family и пропишу переменную Warp Enter.

Перейдём на страничку, откроем Инспектор, обновим страницу. Вот. Никаких ошибок нет. И проинспектируем элементы. Я зайду в тег. И мы видим, что вот у него появилось свойство font family war, font inter. Навожу и вижу подсказку inter fall. То есть этот шрифт подключен, да? Если я его уберу, то, ну, вот какая-то разница есть, но, в принципе, у меня системные шрифты используются, и они очень сильно на inter тоже визуально похожи. Вот. И вот таким образом происходит подключение шрифтов из Google Fonts. То есть первая строка Import from Next Fonts Google. А далее описываете этот шрифт. Не забываем, ну, указать те начертания, которые хотите использовать. Ну, сапсеты, там, кирилицу, латиницу. Далее variable, переменную. И не забываем добавить эту переменную класс name лучше на самый верхний уровень, то есть вот на HTML. Ну и всё. После этого вы можете использовать эту переменную.

Вот, собственно, мне Shirift, ну, не нужен, поэтому я его из Global CSS удалю здесь. А, ну или давайте оставим его, да, вот здесь он пусть остаётся, допустим, тут остаётся просто для примера, то есть того, как это используется. Со шрифтами разобрались.

И далее посмотрим на использование лейаута. Сейчас у нас в проекте есть всего один лейаут, который является главным общим для всего нашего приложения. И все страницы, которые у нас есть, они, естественно, его используют и наследуют. А также можно создавать и дополнительные лейауты для отдельных маршрутов, то есть, чтобы они тоже были добавлены. И мы рассмотрим, как это работает на примере с контактами.

Давайте зайдём в contacts папку и создадим здесь новый файл, который также назовём layout. В моём случае gsx. Далее отсюда экспортируем функцию, ну, компонент по дефолту, который будет являться лейаутом для адреса контакты и всё, что внутри него. То есть я напишу здесь export default function, назову его layout. Ну и, собственно, возвращать он будет JSX разметку. Значит, это layаoutут, поэтому он будет внутри себя отрисовывать тех children, которые мы в него передадим. Поэтому, естественно, из пропсов мы достаем children.

Далее здесь создадим div. Давайте так. Дадим ему класс name. И у меня есть вот такой вот bordered wrapper, такой класс, который просто добавляет рамку. То есть так мы поймём, что этот ди у нас присутствует. Вот. Ну а внутри него мы, значит, разместим children, которые достали из пробсов. Вот. То есть таким образом мы создали дополнительный лейаут для контактов, который обрамляет контент в div с классом Border Wapper и рисует рамку вокруг.

Давайте посмотрим, как это работает. То есть на главную нау это, естественно, никак не повлияло. А в контактах вот у нас появилась вот такая рамка, да, и мы можем перейти посмотреть. В принципе, её и так видно. И вот тег, который идёт из основного лейаута, навигация, подвал из основного лейаута. А далее наш лейаут, который был создан отдельный для контактов, вот он с divвом Bordered Layout. И уже внутри него лежит контент. То есть таким образом в Неджесе вы можете добавлять под лейауты, как бы так сказать, да, отдельные лейауты для отдельных маршрутов. Вот. Единственное, конечно, у вас будет использован и основной лейауты, и внутренние лейауты. Вот. Но вот таким образом вы можете работать с шаблонами и с внешним видом.

Далее рассмотрим использование изображений с помощью компонента image в Next JS. Картинки мы будем размещать на странице about, поэтому сразу перейду сюда.

Далее перейду на рабочий стол и внутри папки с проектом зайду в папку public. То есть здесь у нас размещена графика, и я докину сюда ещё один файл, вот такую картинку, которую буду использовать изображение с автомобилем. А те файлы, которые здесь есть, они нам уже, в принципе, не нужны, поэтому я их удалю. И в папке паблик у меня осталась только одна картинка. То есть тоже разместите какую-то картинку, с которой вы будете работать. А, либо можете опять взять то, что я использую из страницы с материалов к уроку. То есть ссылочка, напоминающаяся в описании. То есть перейти и скачать вот всю эту графику и код, который я использую. О'кей.

Итак, мы закинули картинку в папку public. И как нам теперь её использовать? То есть, в принципе, можно использовать так же, как и раньше в Реакте. Можно просто сделать тег идж и прописать путь. Если картинка будет лежать где-то в папке source, можно её импортировать и прописать. Вот. А можно использовать компонент идж. Но давайте посмотрим разные варианты, чтобы у вас не сложилось впечатление, что в NextТе вот только компонент имиджа и никак больше, да? Хотя компонент имидж - это очень крутая штука, потому что она позволяет оптимизировать картинки, добавлять к ним и зилоуadдинг и оптимизировать по размерам использования. И это очень крутая штука, которая решает, а, ну, несколько проблем или задач с изображениями.

О'кей. Значит, сейчас у нас картинка просто лежит в папке public. То есть, ну, самый простой способ, я могу просто сделать тег, как обычно. А то сш - это у нас как бы, да, идёт в директорию public. И далее указать путь к картинке source. А корневая директория подразумевается пабли, а имя картинки, расширение Alt, например, там Dodge напишу и перейти посмотреть. Отлично. Вот она, картинка отобразилась. Всё, здорово.

Но давайте подключим её через компонент image, который есть в next, как это будет выглядеть. Для этого мы вызываем здесь компонент image, и он мне даёт подсказку. Я вот выбираю image из image. Нажимаю Enter. Вот у меня автоимпорт произошёл. Image пробел сш. Вот так я его закрываю. И далее укажу здесь, значит, атрибуты для вставки картинки. То есть, в принципе, они такие же похожи, как и в TG image, они его дублируют. То есть source с/ Dodge. Ну, в моём случае, как он называется? Dodge 400x200. Я его назвал, чтобы помнить размеры, которые у него указаны, да? Вот так. Там корневая директория, например, так. Или там вот домен основной, да, у нас корень домена. А Dodge 400200 JPEG есть. Далее Alt, то есть нужно указать какое-то описание картинки, потому что иначе он будет ругаться. А, и далее обязательно нужно указать размеры. То есть, если мы остановимся вот на этом этапе, перейдём на страничку, мы увидим сообщение об ошибке, что картинка такая-то и у неё отсутствует обязательная property wids. И he тоже нужно задать. Вот. И давайте зададим. Укажем ширину для картинки. И в моём случае это 400. И хай тоже нужно указать. И в моём случае это 200. Вот перейдём, проверим. Отлично. Вот видим, картинка отображается.

Если мы откроем консоль, посмотрим, тут есть вот такое вот сообщение, что картинка такая-то, значит, была обнаружена как, а, LCP largest Content Full Paint, то есть, ну, как бы большой блок контента. И он рекомендует добавить свойства приорити. А для того, чтобы, значит, эта картинка заранее подгрузилась и была оптимизация её загрузки. Вот поэтому сюда можно добавить просто такой атрибут приорити. Вот, перейти, проверить. Всё, теперь этого сообщения нет, да? И, значит, как бы эта картинка, ну, её загрузка и отображения, они теперь оптимизируются. То есть вот таким образом мы размещаем изображение с помощью компонента imagмиe.

А дальше давайте посмотрим, как добавить картинку, которая, например, находится не нашем сайте, а, например, где-нибудь в сети. То есть для такого примера я возьму картинку с блогаде, то есть одну из обложек к постам, да? Вот есть у меня такая картинка, например, вот давайте даже возьмём не это 2 и вот так просто JPEG. Вот есть вот такая картинка. Я хочу её использовать. Вот она лежит по адресу webcademy.ru. Как мне это сделать? То есть если я создам просто компонент имидж сделаю, да? Давайте здесь бры нарисуем несколько раз просто для отступов. Так, брейклаline, как он интересно его рисует. Вот так. Breakline breakine. И если я просто сделаю тег image и дам ему вот такой вот путь. Давайте скопируем ещё раз. Дам ему путь к картинке в сети. Вот. То картинка, естественно, не отобразится. Там какой-то Alt у неё будет, да, какие-то размеры. Вот 870 на 400, но я сделаю 200 и 435 на 200 будет. Вот если я перейду и посмотрю, картинка не отображается. Я получаю вот такую ошибку. неправильная source property. Вот он адрес host name web academy is not configured under images in your next config JS. То есть, если мы в проекте на Next хотим использовать какие-то картинки из с других хостов и использовать их внутри, ну, использовать их с помощью компонента image, а то тогда нам нужно настроить в Next Config JS настройки для этого хоста для картинок. Выглядеть это будет следующим образом. Идём в Next Config MGS в моём случае. И вот next config, он у меня здесь абсолютно пустой. Мы добавляем сюда images, это объект. Далее указываем remote patterns. Двоеточие - это массив. И здесь для каждого хоста мы можем добавлять отдельное описание. То есть у меня хост всего один. А я указываю протокол https в моём случае. Вот так. И далее указываю host name. Hostname и указываю hostname webcademy.ru. Вот так это будет выглядеть. То есть, если у меня будет ещё какой-то хост, откуда я тоже хочу картинку добавить, я просто напишу запятую и также опишу там протокол иp домен. Вот. Ну, в моём случае только один. А, собственно, теперь переходим и проверяем. Так, обновляем about. Вообще всё сломалось. Ну, в принципе, после правки Next Конфig нужно перезапустить проект. Тут он даже сам остановился. Вот. Поэтому запускаем опять npm rf. Переходим, обновляем и видим, что на страничке about картинка с надписью у нас отображается. В консоли никаких ошибок нет. Вот я обновляю. Всё в порядке.

Минутка внимания. Давайте немножко отвлечёмся. Хочу рассказать о своей авторской школе Вебкадемии, о которой вы, возможно, уже знаете, где я провожу свои авторские курсы по веб-разработке. Это и профессия верстающих сайтов для тех, кто только начинает изучать веб-разработку. Большая программа ФнEND-разработчик, которую я недавно стартовал- это большая программа на 6 месяцев, где мы изучаем всё, что должен знать фронтEN-разработчик на сегодняшний день. Это и JavaScript, React, Next, Node, Express, Typescript, Git, работа в команде и карьерный центр для подготовки к собеседованию, трудоустройству. На этом курсе вы реализуете 12 проектов плюс командную работу, плюс в конце вас ждёт карьерный центр, где мы, а, оформим вам резюме, портфолио, подготовим к собеседованиям, проведём тестовые собеседования, как скрининги с HRМ, так и с техническими специалистами, чтобы вы уверенно отвечали на все необходимые вопросы при трудоустройстве. Ссылочки на мою школу и программы есть в описании под видео. Переходите, ознакамливайтесь. Если есть любые вопросы, а можете задать их или запросить консультацию вот в таких блоках написать нам в Telegram или WhatsApp. Я лично отвечу на ваши сообщения. Ну а на этом всё. Продолжаем изучать Next.

Теперь посмотрим, как использовать феч запросы внутри компонентов NextGS. И используем силу серверных компонентов. Ну не силу, особенность. Дело в том, что если ранее в Реакте для того, чтобы использовать феч внутри компонента и отрисовать какие-то данные, нам нужно было помещать его в вызов в фект, потом получать какое-то значение, записывать его в состояние и потом уже выводить на страницу, то в NextJS так делать не нужно, потому что по умолчанию компоненты серверные и, соответственно, феч происходит на стороне сервера и в клиент, то есть в браузер отдаётся уже готовая страница с готовы HTML, который уже построен на базе того, что было запрошено через феч. Вот. Ну давайте посмотрим, как всё это делается. В общем, это всё гораздо проще, чем было в Реакте.

Итак, для примера я создам страницу users, на которой буду запрашивать список пользователей, которые будут брать с сайта Jon Place Holder. Многие уже знакомы с этим сайтом. То есть такое апи для того, чтобы тестировать проекты. Вот. И тут есть разные ресурсы. И есть вот такой вот маршрут jonplaceholder typeod.com/users, который отдаёт нам вот такой массив с десятью пользователями. Вот. И также ещё здесь мы можем писать users/, например, 1 и получить информацию о пользователе с ID1, да, там, то есть там ID/2, пользователи с ID2 и так далее. Вот, в общем, сейчас мы на вот этом примере будем получать список пользователей через феч. Сделаем это на отдельной страницы. И поэтому я перехожу к нам в проект в директории App. Создаю новую страницу, называю её users. Создаю здесь новый файл, называю его page gsx для страницы users. Пишу SFC, users page могу написать. Далее tab. И возвращаю разметку. Значит, у меня будет здесь такой sectionшн с контентом. Далее будет заголовок. Давайте вот так. Заголовок H2 Users. И давайте добавим ссылку на эту страницу в навигацию Components Navigation. Добавим ссылочку после about. Сделаем ещё одну. Адрес HRF наusers и в названии users. Перейдём на нашу страницу, на сайт и посмотрим. Вот users появился. Я перехожу, вижу users. Сразу откроем консоль, потому что будем смотреть сюда, будем выводить какие-то данные в консоль.

И теперь давайте сделаем запрос на адрес jon placeholder/users и получим вот эти данные. То есть для этого я перехожу в Users page tsx и здесь, собственно, добавляю просто вверху функцию GET Users, асинхронная функция Git users, где я делаю феч на Jon Holder users. Далее вы можете заметить, что вторым аргументом феч сюда передаётся такая настройка Cash no Store. А это передаётся для некста, потому что по умолчанию Next будет кэшировать запросы. Вот у него есть там определённая своя политика кэширования запросов, и можно этим управлять. Ну, в общем, вкратце можно вот так передать no casш, точнее cash шore, и тогда каждый раз при рендере этого компонента, каждый раз будет происходить запрос на пользователей. Вот. Ну, собственно, дальше проверяем. Если результат не о'кей, выбрасываем ошибку и возвращаем rest JSON, то есть данные из формата JSON в JavaScript преобразуем. Это у нас возвращает функция get users. И давайте теперь её мы можем запустить. Например, вот в компонете users page могу создать константу равно и подожду, пока выполнится этот промис фиче get users, так как я здесь использую away, соответственно, здесь тоже должно быть ass, поэтому добавлю вот так. Users page ass в users у меня попадут пользователи. Давайте сделаю consol log users и даже давайте сделаем вот так consol log users запятая users вот и посмотрим теперь в консоль я обновляю страницу и мы видим вот он users распечатанный пользователи и мы видим такой page сервер. То есть сейчас идёт консо распечатко из серверного компонента поэтому мы видим сервер. Вот если мы будем делать консой log в клиентском компоненте, дальше посмотрим про клиентские, там не будет бейджа сервер, то есть просто будет идти распечатка.

И теперь давайте выведем этих пользователей на страницу. Ну, сначала такой самый простой вариант. Например, мы можем обратиться к users.Map и далее, значит, обойти этот массив и вернуть, а, какую-то JSX разметку для каждого пользователя. Например, вернуть div. Тут у нас будет user. У Diva должен быть key какой-то ключ user. А здесь у нас будет какая-то информация. Например, у нас будет user. Вот. Ну посмотрим, что из этого получится. Вот. То есть у нас вот пользователи, да, у каждого есть имя, есть айдишник, там email и так далее. Ну и, собственно, вот имена пользователей я вывел.

Для того, чтобы это было как-то красиво и симпатичней, давайте напишем чуть больше разметки для каждого пользователя, и я вынесу её в отдельный компонент. То есть выше userйage я создам следующий компонент, который назову user item. Const user item равно это у нас будет стрелочная функция, да? И, ну, то есть она у меня будет вызываться вот здесь, да? То есть по сути давайте пока вот я, например, вот это заберу вот и вставлю сюда. Так, ну только здесь return и вот это вот нужно поместить в скобки. Вот так. И аккуратно всё это выровнять. Вот так, да. То есть user item, он возвращает вотюр и принимает user из пропсов, такой компонент. И, соответственно, теперь здесь пишу users map и передаю сюда. Значит, тут, а тут вот таким вот образом вот возвращаю компонент user item, куда передаю пользователя user равно и передаю сюда юзера. Ну и также, чтобы он не ругался, нужно добавить ключ. Ключ равно user.id. Вот перенёс разметку относительно пользователя в отдельный компонент. В общем, всё точно так же, как в реакте. Переходим, проверяем. Отлично. только с той разницы, что сейчас это всё рендерится на сервере.

Ну и для более красивого вывода, значит, вот эту разметку я сейчас внутри немножко поменяю и выглядеть она у меня будет следующим образом. Сейчас выглядеть будет она вот так: div. div. Далее внутри будет, ну, о'кей, мы ему поможем. Вот так. Вот так. И name у меня здесь будет item. Вот таким образом. Так, а на что он ругается? на то, что здесь нету вот этой скобочки в return второй круглой. Отлично. User item выглядит таким образом. Обёртка классный item. Далее один div с описанием пользователя. То есть здесь item, title, user ID, username, а далее user email, а и после этого div идёт просто спан с вот такой стрелочкой вправо. Вот. Ну и по итогу это всё у нас будет выглядеть вот так вот. Вот так вот красиво на странице, чтобы эти пользователи не занимали всю ширину. А для этого вот здесь всех пользователей я помещу в div с классом users list. Есть такой CSS-класс у нас в стилях. Вот так это будет выглядеть. Перейдём, проверим. Отлично. Ну и вот, собственно, список пользователей мы наблюдаем.

Теперь давайте сделаем отдельную страницу под каждого пользователя. То есть как это реализовать. А мы уже познакомились с, скажем так, базовыми маршрутами. То есть когда мы просто пишем имя папки и это является страницей или подстраницей, да? А вот что, если у нас подстраница, она будет как бы с именем параметра. То есть вот как здесь, когда мы запрашиваем какого-то пользователя, user/5, и мы получаем его ID. А в NextJS мы также можем с этим параметром работать. То есть для этого, чтобы работать с параметрами, мы вот здесь в Users можем создать новую папку и указать у неё имя в квадратных скобках и написать ID. То есть таким образом внутри как бы этого маршрута, то есть мы говорим, что мы можем заходить по адресу users/ID, вот. И вот этот вот маршрут ID, э, мы его будем получать, под именем ID и как-то использовать там для отрисовки внутреннего пользователя. Вот. То есть в User/ID я создаю папку, ну, файл, точнее, который будет отображать отдельно пользователя. Давайте создадим здесь новый файл. Также page.x. И здесь также делаем SFC, там, например, users page, да, он будет возвращать какую-то разметку. Ну, пока просто я верну, а, какой-нибудь div и напишу здесь какой-то H1, например, users page, то есть как бы страница отдельного пользователя. Вот. То есть такой маршрут у меня уже есть. Вот компонент под него есть. И в принципе, если сейчас перейти в приложение и в Users, например, ввести с/5, то мы попадём на эту страницу вот users page, да? То есть как бы что бы я здесь не писал, вот asasd, вот я тоже попадаю на эту страницу. То есть как бы users, далее сш вот какой-то параметр, и на эту страницу мы попадаем.

Значит, теперь как этот параметр использовать? Мы его можем достать из параметров. То есть давайте перейдём в Users ID page gx. И вот здесь в пропсах мы скажем, что мы будем доставать params, достаём параметры. И теперь в компоненте из этих параметров можем достать какое-то а значение. То есть ну давайте сделаем consol log и распечатаем params.id. То есть, так как у нас этот параметр называется ID в квадратных скобках, значит, и здесь он ID именуется. И выведем его в консоль. Давайте скажем так: ID двоеточия запятая params ID. И теперь проверим ещё раз. То есть я обновляю страницу, и мы видим, вот я ввёл ID 65465 и вот он ID. Да, введу другой, например, два. Вот он ID 2. Ну и если тут будут какие-то буквы набраны, а это сработает так же. Вот так вот буквы. Собственно, вот он параметр ASD. Вот. И теперь этот параметр мы можем брать и использовать для того, чтобы делать запрос и получать данные о каком-то конкретном пользователе, например, users/ там и а пользователь. Вот. Ну, собственно, давайте это и реализуем. То есть на этой странице выше добавим функцию, которая будет делать запрос и получать данные о конкретном пользователе. А я могу её вообще прокопировать из Users page JSX. То есть у нас вот тут была такая функция get users. А давайте в Users ID page JSX. Я её вставлю, чуть отредактирую, назову её get user. То есть она будет в себя принимать айдишник пользователя. И этот айдишник будет использовать в запросе. Вот здесь запрос я помещу в обратные кавычки. Вот он запрос. И далее здесь интерполяция. И вставлю сюда ID. То есть теперь я буду фечить данные одного этого пользователя. Вот. И там, если не оке, fail to fch user один пользователь, user data. Вот. И как бы отдавать данные об этом пользователе. И здесь очень важно написать сш после users, чтобы запрос был корректный. Вот так. User/ID. И теперь можно запускать функцию Git user, передавать в неё ID и получать информацию о пользователе. Что мы сделаем? Давайте создадим constit get user. Передаём в него params.id. Так как здесь weight, не забываем сделать эту функцию асинхронной, то есть добавить к user page users page. И, собственно, теперь информацию о пользователе мы получили. Давайте пока выведем её в консоль. Вот так. Cons log user/ уже объект user. Перейдём, проверим, посмотрим, что получается. Значит, в адресе запроса я напишу users/1, то есть пишу корректный айдишник такой, который существует у пользователя. И вот в консоли мы видим, да, то есть ID1 был получен. Мы видим сервер USюзер, вот он информация о пользователе с ID1. То есть можем там пять, например, да, там пользователь с ID 5. Вот. Ну давайте теперь каким-то красивым образом а выведем, значит, все эти данные на страницу. То есть вот здесь, где users page, а я, значит, сделаю следующее. Добавлю сюда разметку, которая у меня уже есть готовая. Значит, всё это будет внутри тега section. Ну, как бы не сильно важно. Далее заголовок будет вот такой: H2 section title user и user ID. То есть будет выводиться ID того пользователя, который мы уже получили, да? Вот пять, например, user 5. И далее будут выхо выводиться детали пользователя. Вот, значит, вот так это будет выглядеть. div сame card и card user details очень много деталей то есть имя, username, потом username, user username, email, телефон и так далее, и так далее. Вот. И теперь перейдём, посмотрим. Собственно, вот так вот. Теперь мы можем заходить в user 1, там user 2, user 3 и видеть этих пользователей.

Давайте теперь сделаем так, чтобы у нас была ссылка на каждого пользователя из общего списка пользователей, чтобы мы кликали и переходили на эту страницу. То есть для этого я для этого я закрываю эти страницы. Возвращаюсь к users page GS6. И вот там, где у нас была обёртка на каждом пользователе, я её переделаю в тегли link. Здесь вот linkнк. Нажимаю Enter, чтобы онто импортировал. И далее внутри этого тегали Link я указываю ссылку на, значит, на пользователя. То есть link теперь здесь у меня будет HRF равно. И мы даже укажем вот таким образом. Далее обратные кавычки. Вот. И ссылка будет следующая. То есть как бы в нашем приложении нужно зайти в users/ и далее передать user ID. То есть вот у нас есть объект user, здесь интерполяция user.id, то есть вот такой адрес будет у link для каждого пользователя. Вот. Переходим и проверяем. В принципе, страничка и так сама обновилась. И

Если наводить на пользователей, то внизу слева уже видно подсказку, то есть там корректно все адреса прописаны. Вот, например, два. Нажимаю, и мы видим user 2. Возвращаюсь опять к users, там 6 users, ID 6. То есть отлично. Ссылочки у нас корректно работают.

Также в консоли у нас появились вот такие ворнинги, которые говорят о том, что params should be waited. Короче, нужно запускать параметры через await для того, чтобы потом их использовать. То есть сейчас я просто получаю ID в Users ID page. Я его просто достаю из params. То есть вот просто params как бы, да, взял params ID и его использую. Вот. Но в Next.js вот там же нужно делать await перед тем, как использовать params, использовать ID. Вот, соответственно, здесь можно же вот кликнуть по этой ссылочке, да, как бы перейти в документацию, да, и вот, собственно, пример использования, как мы используем и указано, что это будет провоцировать warning, да, а как нужно использовать, то есть нужно сделать await params и уже через достать из него айдишник, то есть как бы подождать. Вот поэтому, ну, давайте поправим этот код, то есть, ну, поправим следующим образом. Тогда создам const, достану ID из params. Ну, и сделаю await params, то есть вот таким образом. То есть теперь у меня есть ID, которое я могу вот сюда вместо params ID сразу передать ID, которое я подождал и достал из параметров. Вот. Ну, console.log user давайте тоже удалю. То есть лишние console.log нам ни к чему. Вот. И перейдём, обновим страницу. Увидим, что, да, вот отлично. То есть уже этих замечаний, этих ворнингов, их уже нет, и юзеры отображаются корректно.

Идём далее. И по плану у нас вот такие вот шаблоны, как loading, not found и error. То есть в Next.js мы можем создавать подобное лейауту, да, как общий лейаут для общей для всех страниц или как для каких-то конкретных дополнительной. А также мы можем определять, как будет выглядеть страница загрузки, если она будет, да, а как будет выглядеть страница not found, если ничего не найдено, и error страница с ошибкой. Вот. И давайте посмотрим, начнём с loading. И для примера, то есть прямо в app я создам новый файл, который будет называться loading.jsx. Собственно, здесь мы просто описываем компонент, обычный компонент, который просто возвращает разметку, а в которой будет написано loading, то есть, ну, типа загрузка чего-то. Вот, например, SFC. А, назовём его, называем его loading. Вот, экспортируем его по дефолту. И вот то, что он будет возвращать, это будет отображаться во время загрузки страницы. То есть, например, можем сделать div и, например, просто написать loading. Вот, типа, просто какая-то загрузка. И давайте посмотрим, как это работает. То есть теперь при перемещении между страницами, в принципе, любыми, потому что этот loading у меня лежит на уровне app, то есть для всего сайта, он будет отображаться. То есть смотрите, вот видите, я на App main Contacts Users Loading. Ну вот он отображается так очень, да, сразу пропадает очень быстро. Можно, конечно, зайти здесь в сеть. Да, чуть поменьше масштаб. Вот добавить ограничение, например, 4G низкая скорость. Вот обновить страничку. И теперь будем ждать, пока он грузится, да? Ну, в общем, теперь пере вот во время перемещения он чуть подольше отображается, хотя не всегда, но вот, да, видно. Вот, короче говоря, он работает. То есть у нас теперь есть такой общий loading при перемещении вообще между всеми страницами. Сделаем его, значит, чуть-чуть посимпатичнее следующим образом. Стили у меня уже прописаны. Вот. А чтобы он был симпатичный, я просто пропишу, а сюда вот такую разметку. Loader wrapper и name loader. Собственно, это будет рисовать такой определённый кружочек предзагрузки. Так, а ну-ка, получится ли мне его словить? Main about. Вот. Users, да, этот кружочек был. Вот кружочек есть для users, а здесь пока непонятно. А, окей. То есть loading мы добавили, и это как бы общий loading. Вот. А если я добавлю такой же loading где-то в маршруте, например, в Users, то он, получается, перепишет общий loading и будет внутри. То есть, например, если внутри users я тоже добавлю loading, но здесь, например, вместо спиннера я напишу типа просто p и напишу здесь loading users, то логика такая. То есть спиннер будет работать для всех страниц, а вот внутри users будет работать вот этот loading, где будет просто написано loading users. То есть перехожу, проверяю и вот. А в Users, когда я захожу, вот loading users он пишет. Вот. А спиннер здесь он что-то его даже даже не успеваю увидеть, хотя хочется, конечно. Окей, пере попробую перезапустить приложение npm run dev ещё раз. Так, перехожу, обновляю. Вот. Отлично. И теперь наблюдаю спиннер. Да, ну он был там при переходе на страницу контакты. Вот. А внутрь users будет loading users, а между страницами вот будет просто спиннер. Отлично. С этим разобрались, то есть с loading.

А следующий шаблон, который посмотрим, давайте, это будет not found. То есть, а если, например, мы запрашиваем какую-то страницу и она не найдена, чтобы у нас было 404. То есть, что это будет сейчас? Ну, к примеру, если сейчас обращусь по какому-то маршруту, которого не существует, например, ASD, то я получу вот такую страницу 404. This page could not be found, да? То есть вот откуда-то это берётся, то есть ну как бы Next.js её нам отдаёт. Вот. Но мы можем её кастомизировать и сделать её своё. То есть для этого нам нужно просто создать компонент, который будет лежать в файле not-found.jsx. Вот. Ну по аналогии с по аналогии с loading. Давайте я loading просто прокопирую в корне, переименую в not-found.jsx. Далее функцию тоже переименую. Давайте, пусть она будет называться notFound. Вот, в принципе, она экспортится по дефолту, поэтому не имеет значения, как она названа, но пусть будет notFound. И здесь мы просто в теге p скажем not found. То есть у нас будет вот свой текст 404, напишем 404 not found. Вот так будет. И переходим. Так. И теперь проверяем export default loading. Так, что-то он ругается. Ну, я уже пофиксил. То есть он то, что есть, то он и экспортит. Вот, поэтому обновляю страничку. Вот теперь 404 not found. Теперь наша ошибка. То есть как бы, да, вот она будет отображаться, если мы будем запрашивать маршрут, который не существует. Ну и по аналогии с loading и с layout, notFound также может быть изменён в каких-то маршрутах. То есть, например, давайте скопируем этот not-found и вставим его в users. И в not-found мы напишем что-то другое. То есть, например, напишем здесь типа not found this user, типа, да, не получилось найти этого пользователя. Вот так. И теперь, если в users тоже у нас будет какой-то пользователь, которого не получается найти, а то там, ну, мы можем отобразить эту ошибку. Но сейчас тут у нас возникает другой момент. То есть, если я обращаюсь, например, к ID1, к пользователю, которого не существует, да, а то у меня возникает вот такая ошибка. Тут значит с fetch fail to fetch user, то есть там возвращается пустой объект, да? Если мы, например, fetchим или давайте вообще посмотрим, что тут возвращается. Если мы fetchим юзера с ID, который не существует, то, ну да, просто к нам пустой объект приходит. Вот здесь нам нужно просто сделать дополнительную проверку. То есть давайте перейдём в Users ID page.jsx и здесь, когда мы получаем информацию о пользователе, мы проверим, если у нас статус ответа 404. То есть вот как у нас был OK, вот аналогичным образом только мы сделаем вот так. If res.status === 404, то тогда мы можем вызвать функцию notFound, которая есть у Next.js. notFound, которая перенаправит нас на 404. То есть я набираю notFound, он мне подсказывает Next Navigation, нажимаю Enter. Вот он его импортировал выше и запускаю эту функцию. Ну то есть по сути это как бы вот условие, да, вот так можно записать, ну либо в одну строчку. Вот. И проверим теперь. То есть теперь, если у нас нету такого пользователя, то у нас не будет он дальше смотреть, что res не OK, и выбрасывать ошибку. Он будет запускать notFound, и мы будем перенаправляться на 404. Вот, собственно, мы уже это видим. Вот users/100 404 not found this user. То есть вот таким вот удобным, классным образом мы можем управлять и 404 ошибками, перенаправлять куда надо. Вот, пожалуйста. Итак, мы посмотрели ещё один шаблон. То есть мы глянули на loading, в принципе, глянули на not found и посмотрели на error. Ну нет, на error ещё не посмотрели, значит сейчас рассмотрим error. А сейчас, если у нас возникает какая-то ошибка, то мы получаем вот этот вот красный экран. То есть как бы стандартная ошибка от Next.js, да? А, но мы можем и сами что-то отрисовывать, что-то, ну, рисовать на странице, показывать какую-то свою ошибку пользователю и как это будет выглядеть. То есть нужно создать файл error.jsx и также, как loading, notFound, просто возвращать какой-то компонент, который будет рисовать ошибку. То есть, что я сделаю? Ну, я возьму notFound вот в корне в папке app, да, и вот прокопирую его. И давайте error напишу error.jsx. Вот так он должен выглядеть. А опять же тут уже внутри любое имя этой функции. Главное следите, чтобы её же вы экспортировали по дефолту. Вот. И давайте напишем здесь сам error. И я напишу такой global global file. То есть мы создадим несколько errors, чтобы посмотреть, как это работает, да? То есть вот типа глобальный файл с ошибкой. И теперь, да, а ошибка должна быть клиентским компонентом. Собственно, о чём он нам тут и говорит. Вот. error.jsx must be a client component. А use используйте директиву client вверху, да? То есть ошибка должна быть клиентским компонентом. Вот так. "use client". Вот. И теперь будет всё в порядке. То есть, ну, постараемся вызвать какую-то ошибку, например, даже не знаю, как посмотрим что-нибудь. Давайте там about. Вот так. Нет, not found не получается. То есть, ну, если я вызову какую-то ошибку, ну, например, на главной странице, да, например, на главной странице напишу кнопку и напишу у неё onClick, типа onClick, какую-то функцию опишу. Вот. И постараюсь вызвать этим ошибку. У меня теперь вместо главной и вместо красного экрана ошибки отображается файл error. То есть вот сам Error global file. А сама ошибка вот внизу слева я могу кликнуть. Теперь он ругается вот внизу слева. Вот такая история. Ну и опять же вот эти errors, мы их можем создавать и внутри каких-то маршрутов и там прописывать, ну какие-то другие текста, да, и как бы общая ошибка будет одна, а, например, ошибка в users будет другая и так далее. То есть вот так это работает. Но в целом я этот error.jsx удалю. Почему? Потому что, ну, сейчас он как бы скрывает основную ошибку, и, ну, нам в процессе разработки, то есть лучше видеть основную ошибку сразу. Вот поэтому я его error.jsx удалю, да. И теперь, если какая-то ошибка есть, вот мы её сразу наблюдаем. Но в целом с тем, как он работает, мы познакомились, и вы тоже можете добавить его, если захотите, к себе в проект.

[музыка]

Отлично. Мы подошли к следующей интересной теме. Это клиентские компоненты. Клиентские и серверные компоненты. Давайте её рассмотрим. Значит, по умолчанию все компоненты, с которыми мы работали, они серверные. То есть они запускаются на сервере. Сервер, э, их обрабатывает, генерирует всю необходимую разметку и возвращает её в браузер. То есть, когда мы запускаем вот этот вот сайт, вот эта вся разметка, заголовки, абзацы, они уже все приходят в браузер. То есть они не как в React приходит бандл и браузер из него собирает страничку по частям и отображает, да? А тут уже как бы в браузер отдаётся вся разметка всех страниц. Вот. А а теперь клиентские компоненты. То есть когда нам нужны клиентские компоненты. Если мы хотим выполнять действия, которые реализуются на клиенте, то есть, например, события браузера, например, по клику, да, там или scroll, отслеживаем, то есть когда нам нужно работать с браузером - это клиентский компонент. И когда мы запускаем хуки в React, там state, useEffect, тогда этот компонент тоже должен быть клиентским, потому что хуки они работают на клиенте. Вот. И, ну, давайте посмотрим, создадим клиентский компонент. То есть, к примеру, я хочу сделать кнопку и по клику на неё что-то будет происходить. Пусть просто будет какой-то alert, да. Откроем главную страницу. Вот. Добавим сюда кнопку, добавлю button. Вот. И, ну, в button что мы сделаем? Давайте, не знаю, click me. Напишем click me. Вот. И добавлю сюда такую подсказку, типа client event, то есть, да, клиентское событие. И вот, допустим, мы пишем сюда onClick равно фигурные скобки, там какая-то функция у нас будет запускаться. И по клику у нас будет, например, alert срабатывать. И мы будем тут писать "Here we go". Вот так вот. И это сейчас вызовет ошибку. То есть потому что я вызываю onClick в серверном компоненте, а это браузерное поведение, вот браузерное событие. И то есть это должно происходить, это должно происходить на клиенте. Вот. Добавлю сюда ещё class, чтобы она красиво выглядела. И давайте посмотрим, что мы увидим. То есть переходим на главную и видим вот event handlers, то есть, да, обработчики событий могут быть отправлены to client components props в props клиентских компонентов button onClick. А и далее, если вам нужна интерактивность, значит, и согласуйте с использованием клиентского компонента. То есть вот этот функционал onClick, он должен быть в клиентском компоненте. То есть нужно просто создать отдельный компонент, который мы подключим в Page, но он будет клиентским. Вот поэтому в components я создам кнопку, создам компонент Button.jsx. Вот. И перенесу вот эту кнопку в клиентский компонент. Создам SFC Button. Вставлю сюда вот в разметку вот отдельный компонент. И вверху здесь нужно указать такую директиву "use client". Вот, собственно, вот такая строка "use client". Она будет говорить Next.js, что это клиентский компонент. Вот кнопка у меня такая чисто для примера для этого урока. То есть, да, это не какой-то ненастраиваемый компонент, вот props не будет и так далее. Вот просто отдельный компонент клиентский. Всё, теперь эту кнопку я могу использовать здесь. То есть давайте Button. Вот он мне подсказывает откуда. Enter. Пробел слэш. Закрываю. Импортировали. И теперь всё будет корректно работать. То есть переходим на главную, видим, никаких ошибок нет. Вот она наша кнопка. Нажимаем и вот alert "Here we go" отрабатывает. То есть вот таким образом это происходит. Если вам нужно использовать события браузера, а определяете их в отдельные компоненты, выносите, делаете эти компоненты клиентскими, а их уже подключайте в серверные компоненты.

Далее рассмотрим следующие возможности Next.js, и это внутренний API, то есть как мы можем в Next.js создать маршрут для API, прописать туда какие-то функции и вызывать его из из Next.js же и в серверных, и в клиентских компонентах. Давайте посмотрим, как это будет выглядеть. Значит, для того, чтобы создать маршрут для своего такого личного внутреннего API, нужно в папке app создать новую папку, назвать её api. Создаём. Далее в папке мы создаём ещё одну папку, директорию с названием нашего API маршрута. Ну, например, это будет hello. То есть у нас будет запрос к hello. И оттуда, значит, мы можем будем мы сможем делать GET запросы и получать какие-то данные. Вот теперь внутри hello создаём новый файл, который называем как route.js, то есть, ну, JS, потому что не JSX, никакую JSX разметку он не возвращает, но называться он должен именно route в рамках роутера. А, и теперь в этом файле, значит, мы можем писать функции, которые будут обрабатывать GET, POST запросы и так далее. Например, сначала обработаем GET запрос, то есть выглядеть будет следующим образом. Мы пишем функцию, которую мы экспортируем. export async function. Называться она должна GET, потому как будет обрабатывать GET запрос. И что в нашем случае она будет делать? Она будет возвращать какой-то сообщение, да? То есть, если мы GET запрос делаем на hello, то мы получим следующее сообщение. Значит, ответ мы отдаём в таком виде, как NextResponse. Вот NextResponse. Импортируем его из "next/server". Далее у него есть метод json, то есть, чтобы сразу в JSON упаковать объект и объект с данными, например, message и давайте напишем здесь "hello from app". Вот так, да, типа сообщение привет из API. Вот. И теперь мы можем делать GET запрос вот на этот адрес на app/hello. И мы получим "hello from app". Давайте проверим, как это будет выглядеть. Я перемещаюсь в браузер и открою рядом новую страничку и напишу здесь /app/hello. Enter. App/hello. App/hello enter. Отлично. И вот я получил ответ. Message: "hello from app". То есть, ну, по умолчанию стандартным браузером, когда мы делаем запрос, это GET запрос. И вот мы ответ получили. Вот. Ну и, собственно, мы теперь можем сделать этот GET запрос и внутри нашего сайта, и также получить это сообщение. Давайте сделаем. И для демонстрации я создам страницу в рамках нашего сайта, которая будет делать GET запрос на это внутреннее API и получать ответ и выводить. Собственно, для этого перехожу в редактор и создам в app новую папку, которую назову get-example. Вот по такому пути она будет находиться. В ней создаю page.jsx, страницу SFC. Далее дам ей название get-example. А возвращать она будет следующую разметку. Значит, тут у нас будет section с классом content, будет заголовок "GET Request". Давайте сразу ссылочку на неё сделаем в navigation. Вот после users сделаем такой get. И ссылка будет идти на get-example. Get example. Перейду и проверю. Нажимаю на get. Попадаю на get request. Отлично. Ну и теперь внутри страницы get request. Значит, вот здесь напишу GET запрос, который будет идти, который будет идти вот сюда. А, собственно, пишем здесь функцию. Это будет асинхронная функция. async function. async function. Название будет, давайте так. getMessage. getMessageFromAPI. Внутри этой функции мы будем делать fetch, то есть мы будем делать на нашу на наш сайт, то есть на localhost:3000/hello. Вот. И адрес здесь нужно прописывать полностью. То есть я даже его вот так скопирую отсюда. Вот так. localhost:3000/hello. Далее этот fetch, записываем результат в const response. И возвращаем return response.json(). Отлично. Далее запускаем эту функцию уже внутри get-example. Э, так как мы её запускаем и ждём, пока она выполнится, добавляем await, и get-example делаем асинхронной. async. И результат отсюда мы записываем const. Давайте сразу деструктурируем message отсюда. Вот так достанем message. Вот так сразу деструктурируем из вот этого объекта. То есть, а, он нам возвращает такой объект, в котором есть message, мы его message достаём. Вот. Ну и, собственно, теперь здесь давайте выведем. Если у нас есть message, а то тогда то тогда я хочу вывести его вот так в абзаце. {message}. Вот. Перейдём, проверим теперь, как это всё работает. То есть перехожу в браузер. Так, есть ошибка fail to load chunk server call stack и так далее. Так, давайте обновим страничку. Вот. Ну, видимо, во время набора кода, да, он обновлял страницу или что-то. Вот какая-то ошибка была, но сейчас всё в порядке. Get request. Мы видим "Hello from app", то есть наша обычная страница, просто выполнили к нашему же API, а получили от него ответ и вывели на страницу. Вот.

Дальше давайте рассмотрим ещё один пример. То есть сделаем fetch внутри клиентского компонента, да? То есть, например, на этой же странице я создам кнопку по клику на которой, а, у меня будет происходить, ну, пусть тот же запрос, тот же GET запрос. Вот туда же в API. Вот посмотрим, как это будет выглядеть. Ну, по сути, так же, как и в React. То есть для этого я, наверное, прокопирую, возможно, да, возможно, я прокопирую кнопку. Давайте возьмём вот компонент Button. Его прокопирую и переименую этот компонент в ButtonGetRequest. Вот так. ButtonGetRequest он теперь будет называться. Здесь тоже переименую. То есть вот такая у меня кнопка тоже, да, с каким-то event по клику. Давайте подключим её на страницу get-example. То есть идём сюда и вот здесь внизу я хочу подключить Button. Нам подсказывает. Нажимаю Enter. Он её импортировал. Вот. И теперь эта кнопка, она здесь есть. Ну, пока она просто alert "Here we go" выводит. Вот мы же сделаем так, чтобы она делала fetch запрос. То есть, как это будет выглядеть. А внутри этой кнопки мы также делаем функцию, вот, да, как и здесь. То есть делаем функцию getMessageFromAPI. А параметров никаких не будет, то есть fetchим данные. И далее, когда кнопка будет, когда будет происходить клик, мы скажем, что у нас будет запускаться некий clickHandler, который мы сейчас опишем, которого пока нет. Вот здесь выше опишем const clickHandler = async function, внутри которой у нас будет запускаться getMessageFromAPI, собственно, await. Ну, почти всё то же самое, что вот было здесь. То есть message будем из него доставать. Запускаем, достаём сообщение и, ну, можем как-то вывести, например, давайте в alert будем выводить это сообщение, которое мы получили. Вот так. alert(message). Так, onClick. ClickHandler есть. ClickHandler описан. Async. Wait. Так, ну, собственно, вроде бы и всё. Давайте, давайте проверим. Значит, click me. Ну, давайте какой-то другой текст напишем. Давайте напишем сюда "Make GET Request Client". Вот так. Даже откроем консольку, то есть будем смотреть, что нет никаких ошибок. Кликаем и вот "hello from app" alert. То есть это у нас через клиентские компоненты, вот через кнопку. А, ну, собственно, здесь я в запуск fetch, да, не помещал useEffect, потому что он у меня происходит по клику. Если бы просто при рендере компонента сразу бы шёл fetch запрос, я бы тогда его в useEffect поместил, да? И если бы эти данные я выводил на странице, я бы тогда здесь тоже await сделал, положил бы их туда, а потом бы вывел. А так как всё это просто через функцию обработчик работает, поэтому поэтому выглядит таким образом. Вот. И что ещё можно заметить? То есть, когда мы делаем запрос на свой же внутренний API из клиентских компонентов, из "use client" Client, а то вот этот адрес можем не указывать, просто вот так вот. /app/hello, то есть просто переходим, вот, кликаем, вот, и оно работает, всё в порядке. С серверных же компонентов мы так сделать не можем. То есть если в серверном компоненте я просто буду обращаться к /app/hello, давайте посмотрим. Вот, да, пошли ошибки. То есть он ругается fail to parse URL from API hello. Как бы он будет на это ругаться. Поэтому в серверных компонентах нужно полностью указывать адрес. Вот. Ну и, собственно, указываем его так. Ну а для того, чтобы там случае, да, если у вас этот домен поменяется, он, скорее всего, поменяется во время разработки один, на продакшене другой, а стоит его вынести в файл .env.local. То есть давайте создадим файл в корне нашего проекта .env.local и опишем здесь переменную, например, NEXT_APP_URL, ну, даже не API URL, а в принципе URL, как бы нашего проекта, да, NEXT_URL а равно и вот укажем сюда localhost:3000. То есть я его отсюда забираю, вот сюда в кавычки вставляю. Ну, а теперь здесь я изменю обычные кавычки на шаблонные. Вот. И вначале пропишу интерполяцию. И, значит, пропишу использование этой переменной process.env.NEXT_URL. Таким образом переходим, проверяем, обновляем страницу. Отлично. Вот сообщение "Hello from app" в серверном компоненте. То есть всё в порядке. И основной адрес, домен у нас вынесен в .env.local, что теперь удобно. То есть его не нужно держать во всех ну компонентах серверных, которые будут обращаться к API. И пока мы далеко не ушли, хочу сделать один очень важный дисклеймер. Внутренние API, которое мы рассмотрели, используется для того, чтобы с ним работали клиентские компоненты, то есть компоненты, в которых есть "use client" с какими-то интеракциями в нашем проекте, либо работали какие-то внешние сервисы. То есть тот пример, который я сейчас показал, когда создал просто внутреннее API и обратился к нему же через серверный компонент, это антипаттерн, потому что, ну, по сути, как бы создаётся лишние действия из серверного же компонента, обращаясь по API к этому же приложению. То есть это антипаттерн, и лучше так не делать. Как делать стоит, я рассмотрю дальше. Я покажу это перед деплоем приложения. В этом примере, ну, скажем так, это просто наглядная демонстрация, как мы можем вот создать внутреннее API и обратиться к нему. Поэтому берём это на вооружение, смотрим дальше, и дальше будет то, как это правильно делать в приложениях, так чтобы они правильно, корректно деплоились и хорошо работали.

Идём далее. И теперь создадим обработку POST запросов для нашего

AP. Для этого там же впе Hello Road Jess напишем функцию с названием Post. То есть делаю также export syn function. Называться она будет post, она будет принимать какой-то request. И далее мы этот реквест можем получать, обрабатывать, давать пользователю ответ. Ну, собственно, чтобы получить вот то, что было передано в постмассиве, необходимо обратиться к этому реквесту. Ну давайте вызовем Jon для того, чтобы получить тот Jon, который будет туда передан. ait. Собственно, данные мы получаем и записываем в дату. И далее, например, мы будем передавать туда какой-нибудь message или имя, например, имя, да? Соответственно, мы его оттуда достанем. Const name равно из даты достаём имя и используем это имя. И отдадим тоже какое-то hello, только туда добавим это имя. То есть будем делать return next response do jjon. И тут я даже прокопирую вот прямо такой же кусочек. Только тут будет наше имя. То есть hello. Давайте обратную кавычку. Вот так. Обратные кавычки. Hello. Интерполяция dame, то есть идея такая. У нас будет постзапрос, куда мы будем отправлять объект, в котором будет поле name. В name мы будем передавать какое-то имя. Собственно, оно сюда приходит. Мы это имя достаём и используем по строке ответа типа hello name from app. Ещё добавим post request типа, что это прямо вот постзапрос у нас был. Ну и имя будет присутствовать. И теперь создадим страничку, на которой напишем пример запроса сюда получения ответа и вывода. Аналогично с Get example я создам в app новую папку, которую я назову GETP post, точнее нет, Post example. Post example. В ней создаю файл page JSX. Здесь SFC Post example compent. По дефолту я экспортирую. И в postle section далее заголовок post request. Добавим ссылочку в навигации. Вот после get добавим ещё один линк. Пропишем пост. И тут у нас ссылка на postle. Проверим ссылочку. Вот пост есть. Post request. Перехожу. Отлично. И теперь в этой странице post example page gsx пропишем, ну, собственно, да, код, который будет делать postст запрос. Значит, давайте напишем здесь функцию syn function, назовём её make post request. Вот. И что у нас здесь будет? будем фечить, то есть наш адрес/hello методом post и передадим туда объект с property name с каким-то значением. Ну и, собственно, в ответ получим строчку. Вот поэтому пишем следующий код. То есть результат сразу я записываю вс. И тут мы будем делать запуск феча. Aitch. Значит, фечим мы наши сервере. Давайте из Get example я скопирую вот вот эту строчку, чтобы не набирать её заново. Вот. феч. обратные кавычки processv. URL app/hello. Вот это первый аргумент фиче. Второй аргумент объект с настройками. И тут мы уже укажем, что метод у нас будет post. Далее укажем заголовки headers. Headers два точи объект с заголовками. И здесь мы укажем jon, то есть укажем content type content type два точиation/jon jon. Далее после заголовков указываем body. двоеточие body и тело, которое мы передаём, вводим jon jon stringfy от объекта. Вот. И в объекте у нас будет property name. Ну и, например, напишем здесь там, например. Вот. То естьделаем вот такой постзапрос. Вот. Получаем. А далее из результата получаем ответ, то есть rest.jon. записываем это. То есть это у нас уже данные пришли, cons data. Ну и, собственно, можем вернуть эти данные. Можем, так как у нас он возвращает message, можно сразу и месседж как бы отсюда достать, да? Ну давайте просто return return data. Вот. То есть вот такая функция делает фич запрос и получает ответ. Так, здесь ре request. Вот так, по-моему, есть. И теперь давайте вызывать эту функцию. Вызываем её в postle, то есть. Ждём, пока она выполнится. Так как, значит, функцию делаю асинхронной. И сразу достам достану message, как и в прошлый раз мы тоже делали constagж. Сразу достану message из объекта с ответом, да? То есть вот объект с message, а, и этот месседж выведу на страницу, ну, аналогичным образом. То есть, когда будет месседж, тогда его и вывожу вот здесь. в Да, в post request. Вот он. Переходим, проверяем. Так, post request. Вот. Hello, from appost request. Вот. Отлично. То есть мы как бы передали какое-то значение. Наш апе его принял, обработал, отдал ответ, и мы ответ этот вывели. Вот. Клёво. Ну, пока я к уроку готовился, я ещё, то есть, у меня была идея такая вывести всё это ещё как бы наш запрос. Вот, вывести его сюда на страничку красиво. Вот так просто блоком код и пре. Поэтому его сюда добавлю. Ну просто по сути вывод вот этого кода, вот он как бы, да, вот так вот а выводится. О'кей. Работу с АИ мы посмотрели. Гетзапросы, постзапросы из серверных и из клиентских компонентов. И ещё раз сделаю дисклеймер, о котором я уже говорил. То, что мы сейчас рассмотрели, а именно создание своего внутреннего апи для гет, для постзапроса - это нормально. Но оно создаётся и используется для клиентских компонентов. То есть, если у нас есть какая-то форма, мы её можем отправить и, да, к примеру, в клиен в клиентском компоненте мы можем обратиться к этому А. Это нормально. Либо какие-то сторонние, абсолютно другие сервисы могут обращаться к нашему апе. Но в рамках одного этого одного и того же приложения создавать апи, чтобы обратиться к нему же в рамках этого же приложения ещё и постзапросом, это тоже большой антипаттерн. То есть мы вместо того, чтобы просто обратиться к коду этого же приложения, в котором мы вызываем этот серверный компонент, зачем-то идём обходным путём и создаём запрос, аэ, создаём запрос через феч, чтобы обратиться к этому же приложению, да? То есть, ну, так не делается. То есть опять же внутреннее апи, которое делается, оно используется для клиентских компонентов этого приложения либо для сторонних сервисов. Вот то, что было показано здесь, это больше для примера и для демонстрации того, что, ну, наше апи, вот оно есть и оно работает. Как делать правильно будет дальше в этом уроке, в той части, которой в которой я буду готовить проект к продакшену и к публикации на Versal. Поэтому смотрим и не переключаем. Далее посмотрим на работу с метаданными. В стартовом шаблоне это уже было видно, то есть как их можно использовать. И давайте перейдём к App Layout. Вот. И да, я их отсюда даже не удалил. То есть для методанных просто описываем вот такой объект метаa, экспортируем его и в нём описываем данные, собственно, метаданных title, description, keywords. Можно описать метаданные там для разных социальных сетей, а какие-то дополнительные сведения, можно описать Openграph, то есть, ну, давайте немножко как быто немножко чего-то добавим. То есть вот, к примеру, наш общий наши общие метаданные, которые будут у всего сайта, которые прописаны в общем лейауте, да, будут вот такие. Далее, если уже там для какой-то конкретной страницы я хочу добавить свои метаданные, то я также просто их добавляю. То есть вот в Page JSX, да, там с пятнадцатой версии Next JS мне не нужно импортировать метаданные из Next. Я просто пишу вот так export metдаata. Вот. И для главной страницы, например, прописываю вот такие метаданные. Если я хочу для about добавить метаданные, ну, аналогичным образом, то есть просто описываю export constтаata. Вот так. И, пожалуйста, теперь у меня есть метаданные для страницы about. То есть теперь, если я буду перемещаться, смотрите, у нас появилось вот next tutorial, да, на странице about у меня about page написано, ну, и так далее. То есть я могу добавлять как бы, да, отдельные какие-то метаданные вот на другие страницы. То есть вот в ВКонтактах у меня NextJS Tutorial, а в посте у меня create Next App. Ну, это, видимо, ещё немножко старенький, как бы, да, он не пересобрал. Вот. Create next step откуда-то берётся. Ну, в общем, а так, откуда он берётся? Так, раз, ent, contcts. А, потому что здесь layаут. Ну, тут лейаута нет. Вот. О'кей. Ну, может быть, просто пересобрать нужно. А, короче говоря, вот так можно прописывать метаданные. Можно прописывать и Openграф в метаданные. То есть, например, давайте зайдём в layout. Вот здесь, кстати, leng стоит на RU поменять. Вот. И можно, например, сюда добавить типа openгра. Вот так. И в виде объекта также описать, например, там типа сайт name, там tutorial, description, самтекст. Ну, в общем, и так далее. Картинку здесь прописать. Дальше можно уже посмотреть отдельно именно документацию там пографу, какие у него есть поля. Вот. Но а в целом метаданные прописываются вот таким способом. Теперь давайте подготовим наше приложение к prodдакш сборки и его публикации и пофиксим те ошибки, а точнее антипаттерны, которые у нас здесь присутствуют, о которых я говорил ранее в этом уроке. Итак, сейчас у меня запущено наше приложение в режиме npm rdeevelopment сборка. Оно работает, а, всё в порядке, никаких ошибок в консоли нет. Вот get постзапрос, контакты, юзеры. Всё в порядке. Теперь, а, я могу сразу начать фиксить те антипаттерны, которые присутствуют, о которых я уже говорил. Но давайте запустим npm run build, сделаем попытку сборки и увидим, какая ошибка будет. И потом я, собственно, это всё пофикшу и объясню, как должно быть. Итак, а, вызываю консоль, останавливаю сборку. Давайте введу clear. И теперь давайте введём здесь команду npm run. build для того, чтобы собрать готовый билд. Сейчас, собственно, он попытается его собрать, и вот мы получим ошибку. То есть я ввёл npm runild, он его собирает, и он начинает ругаться на страницу. Ну вот post example, он ругается. В принципе, на get example он тоже будет ругаться, просто вот почему-то он решил на пост ругнуться раньше, чем на get. С чем это связано? в компонентах get example, вот странице get example и в post example у нас есть феч запросы к апи. И это феч запросы к нашему же внутреннему апи. То, что это антипаттерн и то, что мы делаем феч запрос от этого приложения к этому же приложению, вместо того, чтобы просто напрямую вызвать здесь какую-то функцию, которая присутствует, мы почему-то делаем феч запрос, чтобы вызвать эту же функцию и получить этот код. То есть это, в принципе, IT паттерн - это первый момент. Вот. А второй момент, почему возникает ошибка, а потому что во время билда, во время сборки билда приложения, а наш сервер, то есть, да, наше вот серверное приложение, оно не запущено и то есть это апи, оно не работает. И то есть как бы идёт запрос на это апе. А так как приложение в этот момент не запущено, ответа нет, приложение не собирается. То есть, если мы даже будем билдить наше приложение и у нас будет феч на какой-то сторонней апе, на абсолютно другой, например, на тот же Jonholder, к слову, а то он должен работать во время билда. То есть, да, можно сказать, у нас же есть вот фечи, например, в Users, где мы фечим Jon Placeholder. Вот, то есть, если Jonholder не будет работать во время сборки нашего приложения, то я тоже получу эту ошибку. Вот, соответственно, поэтому вот здесь у меня ошибка и возникает относительно post example и относительно get example она тоже возникнет. То есть если, ну, например, в по example я сейчас вот этот код закомментирую, да, и вот всё, что с ним связано, не буду делать этот запрос. И давайте сделаем clear и повторю ещё раз. npm run build. Теперь он будет ругаться на page JSX в get example. Вот видим get example. Вот он на него теперь тоже ругается. Вот, потому что опять же сервер не запущен, приложение, да, как бы не работает. И а тут делается феч запрос. То есть он делает этот феч запрос для того, чтобы сделать static site generation для того, чтобы сгенерировать статические страницы вот с статическим контентом. Вот поэтому такой момент возникает. Первый момент, как его можно обойти? То есть что можно сделать, чтобы просто вот избавиться от этого, да? То есть мы можем добавить такую настройку, как export const dynamic, force dynamic. То есть мы можем вот сюда, например, добавить вот в этот компонент get example добавить экспорт вот такой константы dynamic. И то есть таким образом мы переводим эту страницу из SSG, то есть с static Site Generation к SSR, к server Site Rendering. То есть вот когда этой этой страницы нет, ну, точнее, когда вот этой надписи нет настройки, а то тогда next просто сгенерирует там на основе фича как бы нашу страничку, её создаст и будет всё время её отдавать. Static site generation. А, но если мы вот это делаем, то в таком случае у нас будет сервер сайренринг. То есть каждый раз, когда мы её будем запрашивать, он её будет запускать на сервере, запускать этот феч, получать разметку и отдавать её, да? То есть как бы, ну, это делается для производительности. То есть понятно, а SSG он более производительный, оптимизированный, чем SSR, но в то же время SSR он как бы, да, он всегда актуальные данные отдаёт, потому что он всегда будет заново её рендерить, если там есть какие-то фичи. Вот можно ещё указывать период кэша, то есть, например, там в какое время кэш будет устаревать и нужно перерендерить страницу. Это будет уже так называемый ISR в NextJ. Вот. А, ну, если не уходить далеко, а, в эти нюансы, то наличие вот этой инструкции говорит, что это будет SSR. То есть каждый раз, когда мы будем запрашивать, только тогда будет происходить феч. И этот феч не будет происходить при билде страницы. То есть просто быстрый вот такой фикс. Я его просто показываю, чтобы показать, как это работает. Но мы финальное решение у нас будет другое. Ну давайте посмотрим. То есть я делаю npm run build, добавил вот эту инструкцию. Ждём постample. У меня там всё закомментировано. И билд прошёл успешно. То есть вот build прошёл успешно, всё здорово, как бы всё отлично, да? И то есть в пост exampж на самом деле я тоже могу всё это раскомментировать и просто могу добавить вот такую настройку Export Cons Dynamic Force Dynamic. Вот. Но мы так делать не будем. А, то есть мы сделаем по-другому. А мы просто уберём вот этот антипаттерн и как бы, да, те данные, которые, если нам их нужно получить из нашего приложения и которые также могут отдаваться в апе другим сервисам или клиентским приложениям, мы их а опишем по-другому отдельно. Вот поэтому что я сейчас делаю, то есть вот этот эпо dynamic я удалю в post example я верну всё, как было. То есть вот всё возвращаю, как было в начале этой части. как бы у нас и get делает запрос ка и post делает запрос ка. Запускаю development npm run dev enter. Запускаю наше приложение. Вот оно работает, всё в порядке. И теперь давайте избавимся вот этого от этого антипаттерна, когда мы в своём же приложении получаем данные не напрямую, вызывая какие-то функции, а через фечу. Вот. Значит, опять же, это было просто для примера здесь показано, потому что нужно было на это тапе обратиться. О'кей, фиксим. Значит, как мы поступим в этом случае? Я скрою все папки, раскрою вот так source. В папке source я создам директорию, которую назову lip. И далее в директории lip я создам файл, который назову, давайте назовём его hello. Ну, то есть потому что здесь будут функции, которые я использую в app hell, поэтому здесь тоже Hello JS. Вот. И что мы сделаем? То есть те данные, которые мы возвращаем в app, я их буду возвращать в функциях внутри Hello JS. А сам AP, он не будет в себе содержать вот этих вот данных. Он будет содержать только обработку запросов, там, getpost и вызов функции, которые должны эти данные вернуть. Давайте сделаем для начала на примере с GET запросом. То есть в Hello JS мы напишем следующую функцию, которую мы экспортируем. То есть я иду в Hello JS и пишу в нём export function export as function и назову её get hello message, то есть вот, которое мы на get параметр отдаём. И эта функция, она будет собственно возвращать вот то, что нам возвращал метод get. То есть она будет возвращать вот вот этот объект, будет делать return message hello from app. Ну, давайте, так как мы уже немножко изменяем у нас структуру, давайте напишем здесь hell hello from уже не from app, а from shared module. То есть у нас как бы есть некий модуль, который выполняет какую-то работу, возвращает какие-то данные, да, и, ну, share, то есть мы его как бы можем использовать и в апе, и внутри нашего приложения, что мы и будем делать. Вот. А в самом апе мы теперь напрямую не указываем, что мы возвращаем, а когда нам вот нужно здесь что-то вернуть, значит, мы сюда, ну, во-первых, импортируем эту функцию get hello message. Давайте напишем import get hello message from slb/ hello. Мы её импортируем. И, значит, здесь уже внутри GET. Мы её запускаем, а, достаём из неё данные, так как, ну, она у нас сейчас просто вот такая обычная функция, да, и в принципе как бы тут даже асинхронности не обязательно, то есть вот просто обычная функция, но в целом, а, в ней у нас может быть вообще любая логика, она сейчас очень простая, она просто отдаёт заранее прописанный объект, да. Ну, то есть в целом в ней может быть любая логика, какой-то запрос к базе данных, капи. Вот. Да. Поэтому, то есть как бы так я прописываю ASIN, хотя сейчас она не должна быть AS. Ну, то есть в целом в ней может быть какая-то разная логика. Вот. И возможно наш апи будет дёргать эту функцию, а она будет дёргать по апе что-то другое, получать данные, как-то их обрабатывать и отдавать в нашем апе, да? То есть такое может быть. Или она к базе данных может обращаться, что-то оттуда получать. Вот. И потом мы её будем уже вызывать внутриапе. Вот. Поэтому я её как, скажем так, просто на вырос для примера здесь это оставляю. А значит вот get hello message, да, и тогда в общем, там a, ну, ну он тут лишний, да, ну вы поняли. То есть тут может быть асинхронщина какая-нибудь, и тогда делаем assн. Но в моём случае просто обычный объект, поэтому обычная функция. Вот. Э значит просто тогда get hello message запускаю данные, возвращаю в дату и делаю return next response jonata. То есть вот таким образом. И теперь наше апи по getетзапросу, оно само по себе просто обрабатывает getзапрос, дёргает какую-то внутри функцию нашего приложения, которая возвращает данные, да? Если мы обратимся по апе, мы эти данные из из этой функции получим. А сейчас, кстати, всё также должно продолжать работать. Вот мы заходим на get, да, как бы он всё так же работает. А если мы внутри нашего же приложения, вот внутри get example page, хотим эти же данные получить, то мы их уже здесь должны получать по-правильному, не через фечи, через наши апи, через какие-то странные, да, маршруты, зачем лишний феч, а напрямую просто вызываем здесь функцию из lips, которую нам надо, и получаем данные. То есть, а здесь мы что? Импортируем get hello message from lip hello. И вот этого вот у нас не будет, да? То есть там фейчинг, там получение данных и так далее. То есть мы здесь вот просто запускаем get hello message. Она у нас не асинхронная, поэтому просто её запускаю, получаю из неё message, здесь меседж вывожу. Ну и, в принципе, as мне уже здесь не нужен. Вот. И то есть теперь у меня компонент страницы get example просто напрямую сразу задействует функцию в Hello JS и получает те же данные. Вот переходим, проверяем. На один феч запрос на сервере меньше. Вот такая у нас страница. Стоит отметить, что у некста есть определённый ряд правил, по которым он определяет, как будет оптимизироваться рендеринг страницы. Будет это static Site Generation или ISR или SSR. Более подробно все эти вещи мы разбираем на нашем большом курсе по фронтEND-разработке. в его модуле, где мы изучаем NextJS. А для данного урока вполне достаточно того, что мы уже рассмотрели. Открываю файл Post example page GSX. И перед тем, как его отредактировать, мне нужно также поправить сам API и наши функции shared функции в lips. Значит, давайте перейдём в row JS в app и посмотрим, что у нас возвращает пост. То есть у нас постзапрос а работает каким образом? принимает постзапрос, в нём он принимает какие-то данные в дату, из неё извлекает name и на основе этого name формирует новую строчку, которую нам отдаёт. То есть можем сказать, что то, что здесь отвечает за данные - это некая функция, которая принимает данные, в них извлекает нейм и на основе этого нейма формирует строчку. Вот поэтому что мы здесь делаем? Значит, давайте сразу пойдём в Hello JS и напишем здесь вот ещё одну функцию, которая будет формировать данные для постзапроса в Апе. Значит, export export. А также я не буду её делать асинхронной, потому что, ну, она у нас не завязана ни на какую асинхронщину, поэтому просто export function, давайте назовём её create hello greeting. Создать приветствие Hello. Мы знаем, что она будет возвращать строку на основе каких-то данных. То есть она будет принимать в себя имя, да, собственно, то, что нам нужно. И на основе этого имени она будет возвращать нам объект, в котором будет message. Смотрим в ROAD. То есть мы возвращали вот такой вот message: Hello name from IP request. Давайте его просто даже скопирую. Вот. Return message hello name from app post request. Ну и мы сделаем уже не post request, а давайте у нас это будет from shared model и пост, ну как бы постзапрос. Постзапрос. Но если мы будем делать постзапрос, то это будет пост, да, но не всегда. Вот если мы внутри, то это без постзапроса. Вот так. Hello name from shared model у нас будет отлично. И функция, которая возвращает данные, то есть делает обработку полученных данных, вот обрабатывает, возвращает, пусть даже так просто у нас есть. Теперь давайте её импортируем в rad js вот здесь после get hello message. Я её также импортирую из lipo. И теперь в пост. Значит, мы что здесь делаем? Мы из reg jon достали дату, из даты достали. Вот. А-а, далее, то есть, по сути, мы теперь можем запустить функцию create Hello Greeting, передать в него данный name, и по итогу мы получим, ну, те данные, которые нам нужно вернуть. Greeting. Так, более понятно, что у нас внутри лежит гритинг. И, собственно, этот гритинг там у нас, он в виде объекта. Мы его вот сюда, а, в jon передаём. Давайте немножко это подсократим. То есть я сразу вот так вот name отсюда достану, чтобы дату не создавать. И, ну, вот так. Отлично. О'кей. То есть вот таким образом мы поправили обработку постзапроса в а. Ну, а в странице post example pagex мы теперь не делаем этот феchча, то есть мы просто напрямую импортируем сюда функцию из Hello, запускаем её и получаем нужный ответ, обработку данных, да? То есть так же, как и это было, вот так же, как это было в для getзапроса. Давайте напишу, но только не get hello message, а create hello greeting is lip hello. Далее вот эту вот функцию мы удаляем, то есть она нам здесь не нужна. Соответственно, внутри уже самого компонента мы просто вызываем create hello greeting. Это уже никакой нейт. Она у нас сейчас не асинхронная, опять же, она, а, вот здесь вот, да, она может быть асинхронная, она может делать, ну, какие-то ещё запросы в базу данных, в другие апи, обрабатывать данные и так далее, тогда она будет асинхронная. В нашем случае нет. Вот просто её вызываем, значит, да, что-то мы должны были в неё что-то передавать. Давайте посмотрим. Значит, мы, э, передавали вот, то есть вот нам Юри у нас был такой объект, который мы передавали, да? Давайте я его вот здесь пока просто в виде комментария оставлю, эту функцию удалю и, собственно, вот так. Create hellogre запускаю. Aight нам не нужен. Значит, в неё я передаю, да, то есть вот такой объект name в качестве аргумента. Она нам возвращает там какие-то данные, мы месседж из неё достаём. Вот этот пример с кода, он уже не актуален, потому что тут нет никакого запроса. Вот. И здесь в медж, собственно, вот мы месседж выводим. Ну, собственно, так. А, собственно, давайте её ещё раз посмотрим. Здесь у нас просто name, она просто принимает имя и сразу его использует. То есть никакой объект здесь не нужен, поэтому вызывать я её буду вот так просто. Всё, она обработала, вернула приветственное сообщение, и мы его вывели. Вот. Поэтому теперь страница post example page JSX у нас выглядит следующим образом. Давайте

Перейдём, уновим страницу. Итак, get у нас работает, пост за работает, всё в порядке.

Вот просто обращу внимание, что на GET gample у нас остался компонент кнопки клиентский, который делает фейч. И вот как бы всё отлично, всё в порядке, он всё также продолжает работать, мы его никаким образом не меняем. То есть клиентские компоненты, они у нас могут делать запрос к нашему апе. То есть это могут быть формы, кнопки, всё нормально, с этим всё в порядке.

Ну и давайте попробуем собрать это приложение. То есть я открываю терминал, введём и ввожу npm run build. Смотрим. ожидаем. И так как мы всё пофиксили, приложение должно сбилдиться успешно. Отлично. Видим результат. То есть всё, оно было успешно собрано. И для того, чтобы запустить его из билда, нажимаем npm Run Start. Чтобы проверить, что оно корректно работает из билда. npm run start. Нажимаю Enter. Вот оно запускается на local host 3.000. Захожу. И я сразу открою консоль. И смотрите, то есть все странички работают, всё здорово.

И на что? Давайте обратим внимание. То есть вот на странице users, а, когда мы запускали в режиме разработки, там мы видели консоль логи с серверной части. То есть у нас там был B сервер и там список пользователей у нас выводился. Вот когда мы уже сбилдили приложение и запустили его npm Runst, то есть, пожалуйста, серверные логи мы уже не наблюдаем. Всё, вот их нет. Я не помню, у нас, по-моему, не было никаких консольных логов в клиентских компонентах, да? Но если бы они были, мы бы их здесь видели. Вот. Но серверные, вот мы их уже не наблюдаем.

Переходим к финальной части урока, в которой мы сделаем публикацию деплой нашего приложения в сети. Делать мы это будем на сервисе.com. Собственно, и nextjs - это, грубо говоря, одна и та же компания, то есть сервис тех же разработчиков. Вот поэтому всё будет работать максимально нативно и всё здесь довольно легко. Из того, что нам нужно сделать - это загрузить наше приложение в GitРпозитории на GitHub, далее связать Верal с Гитхабом. В Версале уже выбрать нужный проект на Гитхабе, сбилдить его и задеплоить. Собственно, это мы сейчас и сделаем.

У меня открыт веersл, я в нём зарегистрирован. Вот я залогинился. И у меня открыт GitHub, где я тоже залогинен. А далее я сделаю следующее. Создам Git репозиторий в моём приложении Next Tutorial. То есть я перехожу в редактор кода VS-код и ввожу здесь команду Git init Enter. То есть таким образом я создал Git Repпозиitory. Давайте перейдём, посмотрим. В проекте в папке Next Tutorial у меня появилась вот такая скрытая папка тоgit. То есть GitРпозиitory здесь был создан. Внутри VS-кода она у меня скрыта и не отображается. Для этого я открыл папку, чтобы показать, что Git Repпозиitory был создан.

Следующий шаг. У меня есть программа GitHub for Desktop, которая скачана, установлена, и в ней я уже залогинился в свой Git профиil, в тот, куда я буду добавлять этот проект. Собственно, далее в GitHub Desktop я просто закидываю сюда этот проект. Вот next tutorial add repository. Он находит, что этот репозиторий, ну, git репозиторий внутри уже создан. Собственно, вот он видит изменения. Давайте сделаем первый коit. Просто FC напишу first commit. Далее сделаю commit to master. И далее давайте просто опубликуем его. То есть я нажимаю publish repository. Вот имя Nexttutorial на github.com. А можно делать приватный, но я не буду делать его приватным. Возможно, буду давать ссылочку на код после урока. И нажимаю пабish. Ожидаю какое-то время и всё, репозиторий опубликован.

Давайте теперь вернёмся на GitHub. Я обновлю свою главную страничку. И у меня появился вот он репозиторий Next Tutorial. Отлично. Собственно, вот он со всем кодом проекта. Здорово.

Следующий шаг. Теперь я перехожу в Veral, и мне нужно добавить сюда новый проект с Гитба и сделать связку с Гитбом. Давайте выберем Add New Project. Далее он спрашивает, собственно, предлагает импортировать Gitпозиторий. Вот, можно продолжить с гитхабом, гиitлабом, битбакетом и так далее. Продолжаем с гитхабом. Далее здесь он предлагает, значит, авторизовать Верal в моём гитхабе. То есть я залогинен там, и там сейчас нажимаю авторизовать Versal. После этого он предлагает установить GitHub Application. А давайте нажмём Install. Далее на GitHub нажмём Install. Отлично. Готово. Теперь я вижу здесь свой GitHub профиль Devman и вижу свои репозитории. выбираю Next Tutorial и нажимаю импорт.

На следующей странице я могу отредактировать имя проекта. Я его ставлю таким, как есть. Далее здесь есть определённый настройки, которые можно внести для билда. Так как это у нас приложение на NextJS Versal с ним отлично работает, и нет необходимости здесь что-то менять. Также перед деплоем мы сразу можем установить какие-нибудь environment переменные среды окружения. То есть вот то, что у нас было в файле ENV.local, local, да, мы можем что-то сюда добавить, потому что V.local у нас был в Gitignгре и в репозитории у нас не добавился. То есть, естественно, как бы и на сервере его тоже нет, да, и адрес нашего приложения на сервере уже тоже будет другим. Вот поэтому можно сразу сюда какие-то environment variables прописать, если они у вас есть, там доступ к апе, например, или апи ключи. Вот в моём случае их нет. Потом я просто покажу, как их можно добавить в уже существующий проект. Пока я просто закрою эту вкладку. Нажимаем deploy и ждём какое-то время.

Отлично. Наше приложение было успешно собрано, и мы видим вот такое сообщение. Собственно, не зря мы запускали билд перед тем, как подготовить его к публикации. То есть проверили, что оно корректно билдится. И теперь на версали оно тоже корректно сбилдилось и было собрано. Здорово. Далее нажимаем на кнопку continue to dashboard для того, чтобы перейти в панель управления. И, собственно, видим, что наше приложение задеплоилось по вот такому адресу: Next Tutorial и далее какой-то набор символов Versal.appap.

Давайте кликнем по этой ссылочке, перейдём, открывается вот deployment details. И после этого мы можем запустить наше приложение. Мы видим, что оно было создано и те домены, по которым мы можем его открыть. То есть домен для приложения, в моём случае это Next Tutorial, to Murex Versal App. И вот ещё плюс один а технический домен. Вот такой вот тоже есть. То есть можно кликнуть по вот этому домену или можно кликнуть вверху справа по белой кнопке VisзиIT, и наше приложение откроется. В моём случае оно не открывается, пока я не включу VPN. То есть, если долго смотреть на эту страничку, здесь будет ошибка и всё. Но как только я включил VPN, страничка успешно открывается. То есть, давайте покажу ещё раз. Вот я нажимаю на тот домен, который здесь указан, ну либо на кнопку Visit. И, собственно, мы видим вот две новые вкладки, которые были открыты. Вот первый домен и второй. Отлично. Приложение запускается и оно успешно работает. То есть вот наш список пользователей, который из Jon Placehхolder загружается. Вот наш GET Post. Давайте ещё и консоль откроем и проверим, что даже никаких ошибок в консоли у нас нет. Всё в порядке, здорово, about. Главное, супер.

Далее вернёмся в Versal и посмотрим, как мы можем поменять домен для нашего приложения и как мы можем добавить environment variables, переменные среды окружения. А, собственно, итак, я перехожу вAL Next Tutorial. Нажимаю сюда. Далее на вкладке Settings. Слева выбираю пункт domains. И здесь я могу отредактировать домен, который мне дал Versal. То есть, в принципе, он придумывает домен на основе имени репозитория, как мы видим, и дописывает сюда какие-то слова, фразы, чтобы оно было уникальным. Вот, поэтому, конечно, можно добавить свой домен, да, если у вас есть, и привязать его, и тогда ваше приложение уже будет работать на вашем домене, охоститься на Версале. Вот. Но если своего домена нет, а вот этот хочется сделать покрасивее, вы можете нажать edit и попробовать его вот здесь отредактировать. А, добавить что-нибудь, например, давайте 120 я добавлю. То есть вот так я его отредактировал. Далее редиректить не буду. Значит, продакш у нас здесь будет, да? Save. [музыка] Что сделать со старым доменом? Нажму удалить старый домен, то есть будет работать только на новом поддомене. Ну и всё. То есть вот таким образом я поменял домен. И теперь, если я зайду на Next Tutorial 120 Versal App, отлично, моё приложение работает.

Далее опять возвращаюсь в настройки и переходим в раздел здесь же под доменами environment variables. И, собственно, здесь мы можем добавить переменные среды окружения. То есть вот то, что у нас было в нашем проекте, когда мы создавали Next URL. В данном случае он нам, конечно, уже не нужен, да? Допустим, если бы он нам понадобился, то я бы прописал здесь next URL. Ну а в качестве URL я бы прописал вот актуальный домен, который я сейчас сделал новый. Вот такой. Я бы его сюда указал без HTTPs. Вот так. Next. Tutorial 120 Verb. Это вот так бы выглядело. И нажал бы Save. И, пожалуйста, вот внизу environment переменная у меня добавлена, да? То есть какие-то IP-сервисы, к которым могло бы обращаться наше приложение. Например, тот же Jon Placeholder мы могли бы засунуть в а ENV переменную, какие-то апи ключи и так далее.

После того, как мы добавили новые ENV V переменные, нужно сделать редеплой нашего приложения. Конкретно в моём случае я её добавил просто для примера, и она никак не влияет на работу проекта. Но если в вашем случае это переменная в работе приложения используется, то нужно сделать плой, о чём нам и подсказывают внизу справа. Тогда нужно нажать Redeploy, production. Можно даже использовать текущий build CAS. То есть, в принципе, у меня только B variable была добавлена, хотя в моём случае она даже никак не влияет на работу. Отлично. Deployment created. Можно посмотреть deployment, то есть процесс. Вот мы видим статус, building, когда он закончится, здесь будет зелёненькое. И, а, наше приложение можно будет проверять. Если мы посмотрим сейчас, то здесь работает старая версия. Подождали какое-то время, 32 секунды и отлично, статус ready. Наше приложение работает. И теперь мы видим именно последнюю версию, но в моём случае она, конечно, никак не изменилась.

На этом урок по NextJS для новичков подходит к завершению. А если вы хотите изучить фронт-разработку на профессиональном уровне вместе со мной, тогда обратите внимание на мою новую авторскую программу Большой курс Frontend разработчик, на котором мы изучаем JavaScript, React, Next, Typescript, Note, Express и многие другие полезные технологии, которые нужны на сегодняшний день в работе фронт-разработчика. Ссылочка на программу есть в описании под видео. Переходите, ознакамливайтесь, оставляйте заявку на консультацию. Там же можно сразу и записаться. По поводу этого урока. Стартовый код и финальный код также доступны под видео по ссылке в описании. Они будут в моём Telegram-канале, кстати. Подписывайтесь, там я делаю анонсы вот таких уроков и также размещаю полезный контент для фронт-разработчиков. На этом всё. С вами был Юрий Ключевский, школа Вебкадемии. Увидимся на канале. До скорого. [музыка]