Как построить масштабируемую архитектуру для вашего проекта Next.js

Весь код из этого руководства в виде полного пакета доступен в этом репозитории.

Если вы нашли этот урок полезным, пожалуйста, поделитесь им со своими друзьями и коллегами! Чтобы получить больше подобных уроков, вы можете подписаться на Youtube или следить за мной в Twitter.

Этот урок доступен в виде видеоурока, если вы предпочитаете такой формат:

Оглавление

  1. Что такое Next.js?
  2. Введение
  3. Настройка проекта
  4. Блокировка движка
  5. Настройка Git
  6. Форматирование кода и инструменты качества
  7. Git Hooks
  8. Конфигурация кода VS
  9. Отладка
  10. Структура каталогов
  11. Добавление Storybook
  12. Создание шаблона компонента
  13. Использование шаблона компонента
  14. Добавление пользовательского документа
  15. Добавление макетов
  16. Развертывание
  17. Следующие шаги
  18. Подведение итогов

Что такое Next.js?

«Next.js предоставляет вам лучшие возможности для разработчиков со всеми функциями, необходимыми для производства: гибридный статический & серверный рендеринг, поддержка TypeScript, интеллектуальное пакетирование, предварительная выборка маршрутов и многое другое. Конфигурация не требуется».

Как сказано выше, Next.js — это универсальное решение для создания современных приложений на основе полного стека. Оно включает первоклассную поддержку Typescript и React, а также предлагает простые решения для некоторых наиболее распространенных требований к современным приложениям, таких как маршрутизация, API, инструменты postCSS и разделение кода.

Он также поддерживает как создание статических сайтов (для молниеносного создания статических HTML-страниц, которые могут быть размещены где угодно), так и управляемые хостинговые услуги, такие как Vercel/AWS/etc, которые запускают сервер Node и поддерживают полную загрузку данных по требованию и рендеринг страниц на стороне сервера.

Next.js быстро стал одним из самых востребованных навыков в сфере веб-разработки. Этот учебник призван стать своего рода «практическим» дополнением к документации и помочь вам настроить проект, используя множество лучших практик, которые повысят ваши шансы сохранить все управление при масштабировании.

Введение

Этот учебник не предназначен для замены официальной документации, которая является совершенно фантастической. Я настоятельно рекомендую вам прочитать хотя бы раздел «Основные возможности», прежде чем приступать к этому руководству, чтобы вы были знакомы с терминологией, инструментами и некоторыми компонентами, которые они предоставляют и которые являются похожими, но обычно «более мощными» версиями их ванильных HTML-аналогов.

Пожалуйста, просмотрите оглавление, чтобы получить представление о каждой из тем, которых мы коснемся в этом обширном учебнике. Я признаю, что многие из них имеют строгую конфигурацию, и если какие-то из них вам не нравятся, то в большинстве случаев вы можете просто пропустить эти разделы и завершить учебник без особых проблем.

Итак, если вы готовы, давайте приступим!

Настройка проекта

Мы начнем с создания стандартного приложения Next.js с шаблоном Typescript.

npx create-next-app --ts nextjs-fullstack-app-template

cd nextjs-fullstack-app-template
Вход в полноэкранный режим Выход из полноэкранного режима

Сначала мы протестируем, чтобы убедиться, что приложение работает. Мы будем использовать yarn для этого примера, но вы можете с тем же успехом использовать NPM, если захотите.

yarn install

yarn dev
Вход в полноэкранный режим Выйдите из полноэкранного режима

Вы должны увидеть демонстрационное приложение, доступное на http://localhost:3000.

Также рекомендуется запустить

yarn build
Войти в полноэкранный режим Выйти из полноэкранного режима

Чтобы убедиться, что вы сможете успешно выполнить производственную сборку проекта. Рекомендуется (но не обязательно) закрывать ваш dev-сервер при запуске сборки Next.js. В большинстве случаев проблем не возникает, но иногда сборка может перевести ваш dev-сервер в странное состояние, требующее перезапуска.

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

Блокировка движка

Мы хотели бы, чтобы все разработчики, работающие над этим проектом, использовали тот же движок Node и менеджер пакетов, который используем мы. Для этого мы создадим два новых файла:

Мы используем Node v14 Fermium и yarn для этого проекта, поэтому мы установим эти значения следующим образом:

.nvmrc

lts/fermium
Войти в полноэкранный режим Выйти из полноэкранного режима

.npmrc

engine-strict=true
Войти в полноэкранный режим Выйти из полноэкранного режима

Причина, по которой мы используем v14, а не v16 для Node, заключается в том, что позже в учебнике мы будем разворачивать проект на Vercel, который, к сожалению, все еще не поддерживает Node 16. Возможно, к тому времени, когда вы будете читать это руководство, она будет поддерживать. Вы можете следить за прогрессом здесь.

Вы можете проверить вашу версию Node с помощью node --version и убедиться, что вы установили правильную версию. Список кодовых имен версий Node можно найти здесь

Обратите внимание, что использование engine-strict не говорит конкретно о yarn, мы делаем это в package.json:

package.json

  "name": "nextjs-fullstack-app-template",
  "author": "YOUR_NAME",
  "description": "A tutorial and template for creating a production-ready fullstack Next.js application",
  "version": "0.1.0",
  "private": true,
  "license" : "MIT"
  "homepage": "YOUR_GIT_REPO_URL"
  "engines": {
    "node": ">=14.0.0",
    "yarn": ">=1.22.0",
    "npm": "please-use-yarn"
  },
  ...
Вход в полноэкранный режим Выход из полноэкранного режима

В поле engines указываются конкретные версии инструментов, которые вы используете. Вы также можете заполнить свои личные данные, если пожелаете.

Настройка Git

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

По умолчанию в вашем проекте Next.js уже инициализирован репозиторий. Вы можете проверить, на какой ветке вы находитесь, с помощью git status. Там должно быть написано что-то вроде:

On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   README.md

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        .npmrc
        .nvmrc
Войти в полноэкранный режим Выйти из полноэкранного режима

Это говорит о том, что мы находимся на ветке main, и мы еще не ставили и не делали никаких коммитов.

Давайте зафиксируем наши изменения.

git add .

git commit -m 'project initialization'
Вход в полноэкранный режим Выйти из полноэкранного режима

Первая команда добавит и поставит все файлы в каталоге проекта, которые не игнорируются в .gitignore. Вторая сделает коммит состояния вашего текущего проекта с сообщением, которое мы написали после флага -m.

Перейдите на предпочитаемый вами git-хостинг (например, Github) и создайте новый репозиторий для размещения этого проекта. Убедитесь, что ветка по умолчанию имеет то же имя, что и ветка на вашей локальной машине, чтобы избежать путаницы.

На Github вы можете изменить глобальное имя ветки по умолчанию на любое, которое вам нравится, перейдя по ссылке:

Settings -> Repositories -> Repository default branch
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь вы готовы добавить удаленное происхождение вашего репозитория и выполнить push. Github даст вам точные инструкции при его создании. Ваш синтаксис может немного отличаться от моего в зависимости от того, используете ли вы HTTPS, а не SSH.

git remote add origin git@github.com:{YOUR_GITHUB_USERNAME}/{YOUR_REPOSITORY_NAME}.git

git push -u origin {YOUR_BRANCH_NAME}
Войдите в полноэкранный режим Выход из полноэкранного режима

Обратите внимание, что с этого момента мы будем использовать стандарт Conventional Commits и, в частности, соглашение Angular, описанное здесь.

Причина в том, что, как и многие другие функции в этом проекте, мы просто устанавливаем последовательный стандарт для всех разработчиков, чтобы минимизировать время обучения при внесении вклада в проект. Меня лично мало волнует, какой стандарт будет выбран, главное, чтобы все были согласны следовать ему.

Согласованность — это все!

Форматирование кода и инструменты качества

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

  • eslint — Для лучших практик по стандартам кодирования
  • prettier — для автоматического форматирования файлов кода

ESLint

Мы начнем с ESLint, что очень просто, поскольку он автоматически устанавливается и предварительно настраивается в проектах Next.js.

Мы просто добавим немного дополнительных настроек и сделаем его немного более строгим, чем он есть по умолчанию. Если вы не согласны с каким-либо из установленных правил, не беспокойтесь, их очень легко отключить вручную. Мы настраиваем все в файле .eslintrc.json, который уже должен существовать в вашем корневом каталоге:

.eslintrc.json

{
  "extends": ["next", "next/core-web-vitals", "eslint:recommended"],
  "globals": {
    "React": "readonly"
  },
  "rules": {
    "no-unused-vars": [1, { "args": "after-used", "argsIgnorePattern": "^_" }]
  }
}
Вход в полноэкранный режим Выход из полноэкранного режима

В приведенном выше примере кода мы добавили несколько дополнительных значений по умолчанию, мы сказали, что React всегда будет определен, даже если мы специально не импортируем его, и я добавил личное правило, которое мне нравится, которое позволяет вам добавлять префикс переменных с подчеркиванием _, если вы объявили их, но не использовали в коде.

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

Вы можете проверить свою конфигурацию, выполнив команду:

yarn lint
Войти в полноэкранный режим Выйти из полноэкранного режима

Вы должны получить сообщение следующего вида:

✔ No ESLint warnings or errors
Done in 1.47s.
Войти в полноэкранный режим Выйти из полноэкранного режима

Если вы получаете какие-либо ошибки, то ESLint достаточно хорошо объясняет, что это такое. Если вы столкнулись с правилом, которое вам не нравится, вы можете отключить его в «правилах», просто установив значение 1 (предупреждение) или 0 (игнорировать), как показано ниже:

  "rules": {
    "no-unused-vars": 0, // As example: Will never bug you about unused variables again
  }
Войти в полноэкранный режим Выйти из полноэкранного режима

Давайте сделаем коммит на этом этапе с сообщением build: configure eslint.

Prettier

Prettier позаботится об автоматическом форматировании наших файлов. Давайте теперь добавим его в проект.

Он нужен только во время разработки, поэтому я добавлю его как devDependency с -D.

yarn add -D prettier
Вход в полноэкранный режим Выйти из полноэкранного режима

Я также рекомендую вам приобрести расширение Prettier VS Code, чтобы VS Code мог обрабатывать форматирование файлов за вас и вам не нужно было полагаться на инструмент командной строки. Если оно установлено и настроено в вашем проекте, это означает, что VSCode будет использовать настройки вашего проекта, поэтому добавить его здесь все же необходимо.

Мы создадим два файла в корне:

.prettierrc

{
  "trailingComma": "es5",
  "tabWidth": 2,
  "semi": true,
  "singleQuote": true
}
Вход в полноэкранный режим Выход из полноэкранного режима

Эти значения полностью на ваше усмотрение, как лучше для вашей команды и проекта.

.prettierignore

.yarn
.next
dist
node_modules
Вход в полноэкранный режим Выход из полноэкранного режима

В этом файле я поместил список каталогов, на которые Prettier не должен тратить ресурсы. Вы также можете использовать шаблоны типа *.html, чтобы игнорировать группы типов файлов.

Теперь мы добавим новый скрипт в package.json, чтобы можно было запустить Prettier:

package.json

  ...
  "scripts: {
    ...
    "prettier": "prettier --write ."
  }
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь вы можете запустить

yarn prettier
Войти в полноэкранный режим Выйти из полноэкранного режима

для автоматического форматирования, исправления и сохранения всех файлов в вашем проекте, которые вы не проигнорировали. По умолчанию мой форматтер обновил около 5 файлов. Вы можете увидеть их в списке измененных файлов на вкладке контроля исходников слева в VS Code.

Давайте сделаем еще один коммит с build: implement prettier.

Git Hooks

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

Мы будем использовать инструмент под названием Husky.

Husky — это инструмент для запуска скриптов на различных этапах процесса git, например, add, commit, push и т.д. Мы хотели бы иметь возможность задавать определенные условия и разрешать такие действия, как commit и push, только если наш код соответствует этим условиям, предполагая, что это указывает на приемлемое качество нашего проекта.

Чтобы установить Husky, выполните следующие действия

yarn add -D husky

npx husky install
Войдите в полноэкранный режим Выйти из полноэкранного режима

Вторая команда создаст каталог .husky в вашем проекте. Это место, где будут находиться ваши хуки. Убедитесь, что этот каталог включен в вашу копию кода, так как он предназначен для других разработчиков, а не только для вас.

Добавьте следующий скрипт в ваш файл package.json:

package.json

  ...
  "scripts: {
    ...
    "prepare": "husky install"
  }
Вход в полноэкранный режим Выйти из полноэкранного режима

Это обеспечит автоматическую установку Husky при запуске проекта другими разработчиками.

Чтобы создать хук, выполните следующие действия

npx husky add .husky/pre-commit "yarn lint"
Войти в полноэкранный режим Выйти из полноэкранного режима

Выше сказано, что для того, чтобы наш коммит был успешным, сначала должен быть запущен и успешно выполнен скрипт yarn lint. «Успешно» в данном контексте означает отсутствие ошибок. Это позволит вам иметь предупреждения (помните, что в конфигурации ESLint значение 1 — это предупреждение, а 2 — ошибка, на случай, если вы захотите изменить настройки).

Давайте создадим новый коммит с сообщением ci: implement husky. Если всё было настроено правильно, ваш скрипт lint должен запуститься до того, как коммит будет разрешён.

Сейчас мы добавим еще один:

npx husky add .husky/pre-push "yarn build"
Войти в полноэкранный режим Выйти из полноэкранного режима

Вышеуказанное гарантирует, что нам не разрешат выложить в удаленный репозиторий, пока наш код не будет успешно собран. Это кажется вполне разумным условием, не так ли? Не стесняйтесь проверить его, зафиксировав это изменение и попытавшись выполнить push.


Наконец, мы добавим еще один инструмент. До сих пор мы следовали стандартным правилам для всех наших сообщений о фиксации, давайте убедимся, что все в команде также следуют им (включая нас самих!). Мы можем добавить линтер для наших сообщений о фиксации:

yarn add -D @commitlint/config-conventional @commitlint/cli
Войти в полноэкранный режим Выйти из полноэкранного режима

Для настройки мы будем использовать набор стандартных значений по умолчанию, но мне нравится включать этот список в явном виде в файл commitlint.config.js, поскольку я иногда забываю, какие префиксы доступны:

commitlint.config.js

// build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
// ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
// docs: Documentation only changes
// feat: A new feature
// fix: A bug fix
// perf: A code change that improves performance
// refactor: A code change that neither fixes a bug nor adds a feature
// style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
// test: Adding missing tests or correcting existing tests

module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'body-leading-blank': [1, 'always'],
    'body-max-line-length': [2, 'always', 100],
    'footer-leading-blank': [1, 'always'],
    'footer-max-line-length': [2, 'always', 100],
    'header-max-length': [2, 'always', 100],
    'scope-case': [2, 'always', 'lower-case'],
    'subject-case': [
      2,
      'never',
      ['sentence-case', 'start-case', 'pascal-case', 'upper-case'],
    ],
    'subject-empty': [2, 'never'],
    'subject-full-stop': [2, 'never', '.'],
    'type-case': [2, 'always', 'lower-case'],
    'type-empty': [2, 'never'],
    'type-enum': [
      2,
      'always',
      [
        'build',
        'chore',
        'ci',
        'docs',
        'feat',
        'fix',
        'perf',
        'refactor',
        'revert',
        'style',
        'test',
        'translation',
        'security',
        'changeset',
      ],
    ],
  },
};
Войти в полноэкранный режим Выйти из полноэкранного режима

Затем включите commitlint с помощью Husky, используя:

npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
# Sometimes above command doesn't work in some command interpreters
# You can try other commands below to write npx --no -- commitlint --edit $1
# in the commit-msg file.
npx husky add .husky/commit-msg "npx --no -- commitlint --edit '$1'"
# or
npx husky add .husky/commit-msg "npx --no -- commitlint --edit $1"
Войти в полноэкранный режим Выйти из полноэкранного режима

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

Сейчас я создам новый коммит с сообщением ci: implement commitlint.

Вы можете увидеть результат полной кульминации этой настройки на скриншоте ниже, надеюсь, ваш выглядит так же:

Конфигурация VS Code

Теперь, когда мы внедрили ESLint и Prettier, мы можем воспользоваться удобной функциональностью VS Code для их автоматического запуска.

Создайте каталог в корне вашего проекта под названием .vscode и внутри него файл settings.json. Это будет список значений, которые переопределяют настройки по умолчанию установленного VS Code.

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

В settings.json мы добавим следующие значения:

.vscode/settings.json

{
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll": true,
    "source.organizeImports": true
  }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Вышеперечисленное укажет VS Code использовать ваше расширение Prettier в качестве форматера по умолчанию (при желании вы можете переопределить его вручную) и автоматически форматировать ваши файлы и организовывать операторы импорта при каждом сохранении.

Очень удобная вещь, и еще одна вещь, о которой больше не нужно думать, чтобы вы могли сосредоточиться на важных вещах, таких как решение бизнес-задач.

Сейчас я сделаю коммит с сообщением build: implement vscode project settings.

Отладка

Давайте создадим удобную среду для отладки нашего приложения на случай, если мы столкнемся с какими-либо проблемами во время разработки.

Внутри каталога .vscode создайте файл launch.json:

launch.json

{
  "version": "0.1.0",
  "configurations": [
    {
      "name": "Next.js: debug server-side",
      "type": "node-terminal",
      "request": "launch",
      "command": "npm run dev"
    },
    {
      "name": "Next.js: debug client-side",
      "type": "pwa-chrome",
      "request": "launch",
      "url": "http://localhost:3000"
    },
    {
      "name": "Next.js: debug full stack",
      "type": "node-terminal",
      "request": "launch",
      "command": "npm run dev",
      "console": "integratedTerminal",
      "serverReadyAction": {
        "pattern": "started server on .+, url: (https?://.+)",
        "uriFormat": "%s",
        "action": "debugWithChrome"
      }
    }
  ]
}
Вход в полноэкранный режим Выход из полноэкранного режима

С этим скриптом у вас есть три варианта отладки. Нажмите на маленький значок «bug & play icon» слева в VS Code или нажмите Ctrl + Shift + D для доступа к меню отладки. Вы можете выбрать скрипт, который хотите запустить, и запустить/остановить его с помощью кнопок start/stop.

В дополнение к этому, или если вы не используете VS Code, мы также можем установить несколько полезных отладочных скриптов в вашем проекте.

Сначала мы установим cross-env, который понадобится для установки переменных окружения, если ваши соратники работают в разных средах (Windows, Linux, Mac и т.д.).

yarn add -D cross-env
Вход в полноэкранный режим Выход из полноэкранного режима

После установки этого пакета мы можем обновить наш скрипт package.json dev, чтобы он выглядел следующим образом:

package.json

{
  ...
  "scripts": {
    ...
    "dev": "cross-env NODE_OPTIONS='--inspect' next dev",
  },
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Это позволит вам регистрировать данные сервера в браузере во время работы в режиме dev, что облегчит отладку проблем.

На этом этапе я сделаю новый коммит с сообщением build: add debugging configuration.

Структура каталогов

В этом разделе мы рассмотрим настройку структуры папок в нашем проекте. Это одна из тех тем, по поводу которой у многих людей есть очень сильные мнения, и не зря! Структура каталогов может действительно сделать или сломать проект в долгосрочной перспективе, когда она выходит из-под контроля, особенно когда членам команды приходится тратить лишнее время, пытаясь угадать, куда что положить (или найти).

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

/components
/lib
/pages
Вход в полноэкранный режим Выход из полноэкранного режима

У нас будут и другие папки в дополнение к этим для поддержки проекта, но ядро почти всего, что составляет уникальное приложение, которое мы создаем, будет находиться в этих трех директориях.

Внутри components у нас будут подкаталоги, в которых будут сгруппированы похожие типы компонентов. Для этого вы можете использовать любой метод, который вам больше нравится. В свое время я довольно много использовал библиотеку MUI, поэтому я склонен следовать той же организации, которую они используют для компонентов в своей документации.

Например, входы, поверхности, навигация, утилиты, компоновка и т.д.

Вам не нужно создавать эти каталоги заранее и оставлять их пустыми. Я бы просто создавал их по мере создания компонентов.

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

На этом этапе я сделаю коммит с сообщением rfc: create directory structure.

Добавление Storybook

Один из замечательных современных инструментов, доступных нам, если вы еще не знакомы с ним, называется Storybook.

Storybook предоставляет нам среду для демонстрации и тестирования компонентов React, которые мы создаем, вне приложения, в котором мы их используем. Это отличный инструмент для связи разработчиков с дизайнерами и возможности проверить, как разработанные нами компоненты выглядят и функционируют в соответствии с проектными требованиями в изолированной среде без накладных расходов со стороны остальной части приложения.

Обратите внимание, что Storybook предназначен для визуального тестирования, позже мы будем использовать другие инструменты для функционального модульного тестирования и сквозного тестирования.

Лучший способ научиться использовать Storybook — установить его и попробовать!

npx sb init --builder webpack5
Вход в полноэкранный режим Выход из полноэкранного режима

Мы будем использовать версию webpack5, чтобы быть в курсе последних версий webpack (я не знаю, почему он до сих пор не установлен по умолчанию. Возможно, к тому времени, когда вы будете использовать этот учебник, он станет таким).

Когда Storybook устанавливается, он автоматически определяет многие вещи о вашем проекте, например, то, что это приложение React, и другие инструменты, которые вы используете. Он должен сам позаботиться обо всех этих настройках.

Если вы получите запрос о eslintPlugin, вы можете ответить «да». Мы будем настраивать его вручную, поэтому не беспокойтесь, если вы получите сообщение о том, что он не был настроен автоматически.

Откройте файл .eslintrc.json и обновите его на следующий:

.eslintrc.json

{
  "extends": [
    "plugin:storybook/recommended", // New
    "next",
    "next/core-web-vitals",
    "eslint:recommended"
  ],
  "globals": {
    "React": "readonly"
  },
  // New
  "overrides": [
    {
      "files": ["*.stories.@(ts|tsx|js|jsx|mjs|cjs)"],
      "rules": {
        // example of overriding a rule
        "storybook/hierarchy-separator": "error"
      }
    }
  ],
  "rules": {
    "no-unused-vars": [1, { "args": "after-used", "argsIgnorePattern": "^_" }]
  }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Я добавил // New, чтобы отметить два новых раздела и строки, специфичные для Storybook.

Вы заметите, что Storybook также добавил каталог /stories в корень вашего проекта, в котором содержится ряд примеров. Если вы новичок в Storybook, я настоятельно рекомендую вам просмотреть их и оставить до тех пор, пока вам не станет удобно создавать свои собственные без шаблонов.

Перед запуском нам нужно убедиться, что мы используем webpack5. Добавьте следующее в ваш файл package.json:

package.json

{
  ...
  "resolutions": {
    "webpack": "^5"
  }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Затем запустите

yarn install
Войти в полноэкранный режим Выйти из полноэкранного режима

Чтобы убедиться, что webpack5 установлен.

Далее нам нужно обновить файл .storybook/main.js:

storybook/main.js

module.exports = {
  stories: ['../**/*.stories.mdx', '../**/*.stories.@(js|jsx|ts|tsx)'],
  /** Expose public folder to storybook as static */
  staticDirs: ['../public'],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    '@storybook/addon-interactions',
  ],
  framework: '@storybook/react',
  core: {
    builder: '@storybook/builder-webpack5',
  },
};
Enter fullscreen mode Выйти из полноэкранного режима

Здесь мы изменили шаблон для файлов stories так, чтобы он подхватывал любые файлы .stories внутри наших каталогов компонентов (или других).

Мы также открыли папку «public» Next.js как статическую директорию, чтобы мы могли тестировать в Storybook такие вещи, как изображения, медиа и т.д.

Наконец, прежде чем запустить сам Storybook, давайте добавим несколько полезных значений в storybook/preview.js. Это файл, в котором мы можем управлять настройками по умолчанию для отображения наших историй.

storybook/preview.js

import '../styles/globals.css';
import * as NextImage from 'next/image';

const BREAKPOINTS_INT = {
  xs: 375,
  sm: 600,
  md: 900,
  lg: 1200,
  xl: 1536,
};

const customViewports = Object.fromEntries(
  Object.entries(BREAKPOINTS_INT).map(([key, val], idx) => {
    console.log(val);
    return [
      key,
      {
        name: key,
        styles: {
          width: `${val}px`,
          height: `${(idx + 5) * 10}vh`,
        },
      },
    ];
  })
);

// Allow Storybook to handle Next's <Image> component
const OriginalNextImage = NextImage.default;

Object.defineProperty(NextImage, 'default', {
  configurable: true,
  value: (props) => <OriginalNextImage {...props} unoptimized />,
});

export const parameters = {
  actions: { argTypesRegex: '^on[A-Z].*' },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  },
  viewport: { viewports: customViewports },
};
Войти в полноэкранный режим Выход из полноэкранного режима

Здесь есть несколько личных предпочтений, но вы можете настроить его так, как вам удобно. Обязательно установите точки останова по умолчанию в соответствии с тем, что важно для вас в вашем приложении. Мы также добавляем обработчик, чтобы Storybook мог обрабатывать компонент Next <Image> без сбоев.

Теперь мы готовы к тестированию. Запустите:

yarn storybook
Войти в полноэкранный режим Выйдите из полноэкранного режима

Если все идет хорошо, вы увидите сообщение в консоли, которое выглядит следующим образом:

И вы сможете получить доступ к нему на http://localhost:6006.

Я бы рекомендовал вам поиграть и ознакомиться с примерами, если вы никогда не использовали его раньше.

На этом этапе я сделаю коммит с сообщением build: implement storybook.

Создание шаблона компонента

Пришло время собрать воедино всю конфигурацию, которую мы сделали, и посмотреть, как мы можем создать и реализовать наш первый компонент, используя стандарты, которые мы для себя установили.

Мы просто создадим простую карточку. Создадим следующую структуру каталогов:

/components/templates/base

И внутри этой директории мы создадим BaseTemplate.tsx. Он будет следовать стандартной схеме соответствия имен файлов каталогам, ведущим к нему. Это позволит нам, например, иметь другие типы карт в директории cards, такие как PhotoCard или TextCard и т.д.

BaseTemplate.tsx

export interface IBaseTemplate {}

const BaseTemplate: React.FC<IBaseTemplate> = () => {
  return <div>Hello world!</div>;
};

export default BaseTemplate;
Вход в полноэкранный режим Выход из полноэкранного режима

Каждый из наших компонентов будет следовать именно такой структуре. Даже если он не использует props, он все равно будет экспортировать пустой интерфейс props для компонента. Причина в том, что это позволит нам воспроизвести эту точную структуру во многих компонентах и файлах, и обмениваться компонентами/импортами, используя один и тот же ожидаемый шаблон и просто находя/заменяя имена компонентов.

Когда вы начнете работать с историями, макетами реквизитов и т.д., станет быстро очевидно, насколько удобно и эффективно поддерживать последовательную схему именования и интерфейс для всех файлов компонентов.

Это возвращает нас к тому, что последовательность — это все, о чем мы говорили ранее.

Далее я собираюсь создать файл модуля стиля, который будет жить рядом с компонентом. По умолчанию Next.js предоставляет вам каталог /styles, который я лично не использую, но если вы предпочитаете хранить все свои стили в одном месте, это отличный выбор. Я просто предпочитаю хранить их вместе с компонентами.

BaseTemplate.module.css

.component {
}
Вход в полноэкранный режим Выход из полноэкранного режима

Стандартный пустой шаблон для размещения стилей верхнего уровня в компоненте. Вы можете обновить свой BaseTemplate следующим образом:

BaseTemplate.tsx

import styles from './BaseTemplate.module.css';

export interface IBaseTemplate {}

const BaseTemplate: React.FC<IBaseTemplate> = () => {
  return <div className={styles.container}>Hello world!</div>;
};

export default BaseTemplate;
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь у нас есть чистый шаблон для стилей.

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

BaseTemplate.tsx

import styles from './BaseTemplate.module.css';

export interface IBaseTemplate {
  sampleTextProp: string;
}

const BaseTemplate: React.FC<IBaseTemplate> = ({ sampleTextProp }) => {
  return <div className={styles.container}>{sampleTextProp}</div>;
};

export default BaseTemplate;
Войти в полноэкранный режим Выйти из полноэкранного режима

Для каждого создаваемого нами компонента нам понадобится очень быстрый и простой способ протестировать его в различных средах (например, в Storybook, а также в приложении и, возможно, в наших модульных тестах). Будет удобно иметь быстрый доступ к данным для рендеринга компонента.

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

BaseTemplate.mocks.ts

import { IBaseTemplate } from './BaseTemplate';

const base: IBaseTemplate = {
  sampleTextProp: 'Hello world!',
};

export const mockBaseTemplateProps = {
  base,
};
Вход в полноэкранный режим Выход из полноэкранного режима

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

Теперь давайте создадим историю для этого компонента:

BaseTemplate.stories.tsx

import { ComponentStory, ComponentMeta } from '@storybook/react';
import BaseTemplate, { IBaseTemplate } from './BaseTemplate';
import { mockBaseTemplateProps } from './BaseTemplate.mocks';

export default {
  title: 'templates/BaseTemplate',
  component: BaseTemplate,
  // More on argTypes: https://storybook.js.org/docs/react/api/argtypes
  argTypes: {},
} as ComponentMeta<typeof BaseTemplate>;

// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
const Template: ComponentStory<typeof BaseTemplate> = (args) => (
  <BaseTemplate {...args} />
);

export const Base = Template.bind({});
// More on args: https://storybook.js.org/docs/react/writing-stories/args

Base.args = {
  ...mockBaseTemplateProps.base,
} as IBaseTemplate;
Вход в полноэкранный режим Выход из полноэкранного режима

Я не буду вдаваться в подробности того, что включает в себя каждая часть файла stories, для этого лучше всего обратиться к официальной документации Storybook.

Цель здесь — создать последовательный, легко копируемый/вставляемый шаблон построения и тестирования компонентов.

Давайте попробуем это сделать. Запуск:

yarn storybook
Войти в полноэкранный режим Выйти из полноэкранного режима

Если все идет хорошо, вас встретит прекрасно выглядящий базовый компонент (если нет, я советую вам вернуться к предыдущему разделу и проверить, не пропустили ли вы какие-либо настройки).

Теперь, когда мы начинаем создавать больше файлов, полезно взять за привычку запускать yarn lint перед фиксацией, чтобы убедиться, что все чисто и готово к работе. Я собираюсь сделать коммит с сообщением build: create BaseTemplate component.

Использование шаблона компонента

Поскольку у нас есть наш шаблон, давайте пройдемся по процессу его использования для создания реального компонента.

Создайте каталог components/cards. Затем скопируйте всю директорию base из templates в cards и переименуйте ее в cat. Мы собираемся создать CatCard. Переименуйте каждый из файлов, чтобы они соответствовали друг другу. Когда все будет готово, это должно выглядеть следующим образом:

Теперь вы можете нажать ctrl + shift + F (или эквивалент mac) в VS Code, чтобы выполнить полный поиск и замену проекта. Включите только components/cards/cat и сделайте замену для CatCard, чтобы заменить BaseTemplate. Это должно выглядеть следующим образом:

Теперь вы готовы к работе, у вас есть чистый предварительно сгенерированный шаблон для работы, который включает в себя историю и макет данных для вашей карты. Довольно удобно! Давайте сделаем ее похожей на настоящую карточку:

(Для справки: я не создавал эту красивую открытку, она основана на примере, созданном здесь талантливым Лионом Это)

CatCard.tsx

import styles from './CatCard.module.css';
import Image from 'next/image';

export interface ICatCard {
  tag: string;
  title: string;
  body: string;
  author: string;
  time: string;
}

const CatCard: React.FC<ICatCard> = ({ tag, title, body, author, time }) => {
  return (
    <div className={styles.container}>
      <div className={styles.card}>
        <div className={styles.card__header}>
          <Image
            src="/time-cat.jpg"
            alt="card__image"
            className={styles.card__image}
            width="600"
            height="400"
          />
        </div>
        <div className={styles.card__body}>
          <span className={`${styles.tag} ${styles['tag-blue']}`}>{tag}</span>
          <h4>{title}</h4>
          <p>{body}</p>
        </div>
        <div className={styles.card__footer}>
          <div className={styles.user}>
            <Image
              src="https://i.pravatar.cc/40?img=3"
              alt="user__image"
              className={styles.user__image}
              width="40"
              height="40"
            />
            <div className={styles.user__info}>
              <h5>{author}</h5>
              <small>{time}</small>
            </div>
          </div>
        </div>
      </div>
    </div>
  );
};

export default CatCard;
Войдите в полноэкранный режим Выйти из полноэкранного режима

Установите стили:

CatCard.module.css

@import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@300..700&display=swap');

.container {
  margin: 1rem;
}

.container * {
  box-sizing: border-box;
  padding: 0;
  margin: 0;
}

.card__image {
  max-width: 100%;
  display: block;
  object-fit: cover;
}

.card {
  font-family: 'Quicksand', sans-serif;
  display: flex;
  flex-direction: column;
  width: clamp(20rem, calc(20rem + 2vw), 22rem);
  overflow: hidden;
  box-shadow: 0 0.1rem 1rem rgba(0, 0, 0, 0.1);
  border-radius: 1em;
  background: #ece9e6;
  background: linear-gradient(to right, #ffffff, #ece9e6);
}

.card__body {
  padding: 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.tag {
  align-self: flex-start;
  padding: 0.25em 0.75em;
  border-radius: 1em;
  font-size: 0.75rem;
}

.tag-blue {
  background: #56ccf2;
  background: linear-gradient(to bottom, #2f80ed, #56ccf2);
  color: #fafafa;
}

.card__body h4 {
  font-size: 1.5rem;
  text-transform: capitalize;
}

.card__footer {
  display: flex;
  padding: 1rem;
  margin-top: auto;
}

.user {
  display: flex;
  gap: 0.5rem;
}

.user__image {
  border-radius: 50%;
}

.user__info > small {
  color: #666;
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Установите данные макета:

CatCard.mocks.ts

import { ICatCard } from './CatCard';

const base: ICatCard = {
  tag: 'Felines',
  title: `What's new in Cats`,
  body: 'Lorem ipsum dolor sit amet consectetur adipisicing elit. Sequi perferendis molestiae non nemo doloribus. Doloremque, nihil! At ea atque quidem!',
  author: 'Alex',
  time: '2h ago',
};

export const mockCatCardProps = {
  base,
};
Войти в полноэкранный режим Выйти из полноэкранного режима

Обратите внимание, что здесь используется изображение кошки (/time-cat.jpg) из публичной директории проекта. Вы можете найти его в репозитории проекта.

Единственное, что нам нужно обновить в CatCard.stories, это изменить название истории с templates/CatCard на cards/CatCard.

Нам нужно обновить наш next.config.js, потому что мы используем домен, который мы не указали в явном виде как разрешенный (для аватара). Просто обновите файл конфигурации, чтобы он выглядел следующим образом:

next.config.js

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
  images: {
    domains: ['i.pravatar.cc'],
  },
};

module.exports = nextConfig;
Войти в полноэкранный режим Выйти из полноэкранного режима

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

Теперь запустите Nun Storybook, и, если вам повезет, вас встретят:

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

pages/index.tsx

import type { NextPage } from 'next';
import Head from 'next/head';
import Image from 'next/image';
import CatCard from '../components/cards/cat/CatCard';
import { mockCatCardProps } from '../components/cards/cat/CatCard.mocks';
import styles from '../styles/Home.module.css';

const Home: NextPage = () => {
  return (
    <div className={styles.container}>
      <Head>
        <title>Create Next App</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <main className={styles.main}>
        <h1 className={styles.title}>
          Welcome to <a href="https://nextjs.org">Next.js!</a>
        </h1>

        <div className={styles.grid}>
          <CatCard {...mockCatCardProps.base} />
          <CatCard {...mockCatCardProps.base} />
          <CatCard {...mockCatCardProps.base} />
          <CatCard {...mockCatCardProps.base} />
        </div>
      </main>

      <footer className={styles.footer}>
        <a
          href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
          target="_blank"
          rel="noopener noreferrer"
        >
          Powered by{' '}
          <span className={styles.logo}>
            <Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
          </span>
        </a>
      </footer>
    </div>
  );
};

export default Home;
Вход в полноэкранный режим Выход из полноэкранного режима

Давайте посмотрим на финальный шедевр:

yarn dev
Войти в полноэкранный режим Выход из полноэкранного режима

Добавление пользовательского документа

Хотя на данном этапе в этом нет необходимости, вы, вероятно, захотите иметь более тонкий контроль над тем, что находится в <head> вашего приложения. Создание пользовательского _document.tsx в каталоге pages позволит вам это сделать. Создайте этот файл сейчас.

pages/_document.tsx

import Document, { Head, Html, Main, NextScript } from 'next/document';

class MyDocument extends Document {
  render() {
    return (
      <Html>
        <Head>
          <link rel="preconnect" href="https://fonts.googleapis.com" />
          <link rel="preconnect" href="https://fonts.gstatic.com" />
          <link
            href="https://fonts.googleapis.com/css2?family=Quicksand:wght@300..700&display=swap"
            rel="stylesheet"
          />
        </Head>
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

export default MyDocument;
Вход в полноэкранный режим Выход из полноэкранного режима

Обратите внимание, что я удалил шрифт URL @import из components/cards/cat/CatCard.module.css и поместил этот шрифт Google сюда, в голову, для предварительной загрузки.

Все остальное, что вам нужно сделать или настроить в элементе <head>, теперь можно сделать в этом файле.

Имейте в виду, что этот <Head> не такой же, как тот, который импортируется из next/head. Они оба будут работать вместе, а этот используется исключительно для данных, которые вы хотите загружать на каждой странице.

Более подробную информацию о том, как использовать пользовательский _document смотрите в документации.

Добавление макетов

Макеты — это важная концепция в Next.js. Они помогают управлять состоянием между страницами. В этом разделе мы будем использовать тот же базовый шаблон, что и в официальном примере, и просто адаптируем его под наш проект.

Создайте новый каталог layouts в components. Мы будем копировать наш каталог templates/case еще два раза. Один для вызоваprimary и один для вызова sidebar. По завершении все должно выглядеть следующим образом:

Выполните поиск/замену с учетом регистра для BaseTemplate внутри каждого из файлов, чтобы заменить его на PrimaryLayout и SidebarLayout соответственно.

Если у вас возникнут трудности с этим шагом, не стесняйтесь просто взять структуру из репозитория.

Все заслуги перед _leerob и JJ Kasper из Vercel за структуру этих шаблонов макетов_.

Обновите содержимое PrimaryLayout.tsx и PrimaryLayout.module.css:

components/layouts/primary/PrimaryLayout.tsx

import Head from 'next/head';
import styles from './PrimaryLayout.module.css';

export interface IPrimaryLayout {}

const PrimaryLayout: React.FC<IPrimaryLayout> = ({ children }) => {
  return (
    <>
      <Head>
        <title>Primary Layout Example</title>
      </Head>
      <main className={styles.main}>{children}</main>
    </>
  );
};

export default PrimaryLayout;
Вход в полноэкранный режим Выход из полноэкранного режима

components/layouts/primary/PrimaryLayout.module.css

.main {
  display: flex;
  height: calc(100vh - 64px);
  background-color: white;
}

.main > section {
  padding: 32px;
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Затем для боковой панели:

components/layouts/sidebar/SidebarLayout.tsx

import Link from 'next/link';
import styles from './SidebarLayout.module.css';

export interface ISidebarLayout {}

const SidebarLayout: React.FC<ISidebarLayout> = () => {
  return (
    <nav className={styles.nav}>
      <input className={styles.input} placeholder="Search..." />
      <Link href="/">
        <a>Home</a>
      </Link>
      <Link href="/about">
        <a>About</a>
      </Link>
      <Link href="/contact">
        <a>Contact</a>
      </Link>
    </nav>
  );
};

export default SidebarLayout;
Войти в полноэкранный режим Выйти из полноэкранного режима

components/layouts/sidebar/SidebarLayout.module.css

.nav {
  height: 100%;
  display: flex;
  flex-direction: column;
  width: 250px;
  background-color: #fafafa;
  padding: 32px;
  border-right: 1px solid #eaeaea;
}

.nav > a {
  margin: 8px 0;
  text-decoration: none;
  background: white;
  border-radius: 4px;
  font-size: 14px;
  padding: 12px 16px;
  text-transform: uppercase;
  font-weight: 600;
  letter-spacing: 0.025em;
  color: #333;
  border: 1px solid #eaeaea;
  transition: all 0.125s ease;
}

.nav > a:hover {
  background-color: #eaeaea;
}

.input {
  margin: 32px 0;
  text-decoration: none;
  background: white;
  border-radius: 4px;
  border: 1px solid #eaeaea;
  font-size: 14px;
  padding: 8px 16px;
  height: 28px;
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь, когда эти шаблоны созданы, нам нужно их использовать. Мы обновим нашу страницу Home и создадим другую страницу под названием about.tsx, чтобы показать, как использовать общие макеты и сохранять состояние компонентов между страницами.

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

pages/page.d.ts

import { NextPage } from 'next';
import { ComponentType, ReactElement, ReactNode } from 'react';

export type NextPageWithLayout<P = {}> = NextPage<P> & {
  getLayout?: (_page: ReactElement) => ReactNode;
  layout?: ComponentType;
};
Вход в полноэкранный режим Выйти из полноэкранного режима

Теперь вы можете использовать этот интерфейс NextPageWithLayout вместо NextPage, когда вам нужно создать страницы с пользовательскими макетами.

Теперь давайте обновим нашу главную страницу:

pages/index.tsx

import CatCard from '../components/cards/cat/CatCard';
import { mockCatCardProps } from '../components/cards/cat/CatCard.mocks';
import PrimaryLayout from '../components/layouts/primary/PrimaryLayout';
import SidebarLayout from '../components/layouts/sidebar/SidebarLayout';
import styles from '../styles/Home.module.css';
import { NextPageWithLayout } from './page';

const Home: NextPageWithLayout = () => {
  return (
    <section className={styles.main}>
      <h1 className={styles.title}>
        Welcome to <a href="https://nextjs.org">Next.js!</a>
      </h1>
      <CatCard {...mockCatCardProps.base} />
    </section>
  );
};

export default Home;

Home.getLayout = (page) => {
  return (
    <PrimaryLayout>
      <SidebarLayout />
      {page}
    </PrimaryLayout>
  );
};
Вход в полноэкранный режим Выйти из полноэкранного режима

а также создадим новую страницу about в каталоге pages:

pages/about.tsx

import PrimaryLayout from '../components/layouts/primary/PrimaryLayout';
import SidebarLayout from '../components/layouts/sidebar/SidebarLayout';
import { NextPageWithLayout } from './page';

const About: NextPageWithLayout = () => {
  return (
    <section>
      <h2>Layout Example (About)</h2>
      <p>
        This example adds a property <code>getLayout</code> to your page,
        allowing you to return a React component for the layout. This allows you
        to define the layout on a per-page basis. Since we&apos;re returning a
        function, we can have complex nested layouts if desired.
      </p>
      <p>
        When navigating between pages, we want to persist page state (input
        values, scroll position, etc.) for a Single-Page Application (SPA)
        experience.
      </p>
      <p>
        This layout pattern will allow for state persistence because the React
        component tree is persisted between page transitions. To preserve state,
        we need to prevent the React component tree from being discarded between
        page transitions.
      </p>
      <h3>Try It Out</h3>
      <p>
        To visualize this, try tying in the search input in the{' '}
        <code>Sidebar</code> and then changing routes. You&apos;ll notice the
        input state is persisted.
      </p>
    </section>
  );
};

export default About;

About.getLayout = (page) => {
  return (
    <PrimaryLayout>
      <SidebarLayout />
      {page}
    </PrimaryLayout>
  );
};
Войти в полноэкранный режим Выйдите из полноэкранного режима

Затем обновите _app.tsx следующим образом:

pages/_app.tsx

import type { AppProps } from 'next/app';
import './globals.css';
import { NextPageWithLayout } from './page';

interface AppPropsWithLayout extends AppProps {
  Component: NextPageWithLayout;
}

function MyApp({ Component, pageProps }: AppPropsWithLayout) {
  // Use the layout defined at the page level, if available
  const getLayout = Component.getLayout || ((page) => page);

  return getLayout(<Component {...pageProps} />);
}

export default MyApp;
Войти в полноэкранный режим Выйдите из полноэкранного режима

Наконец, в файлах mocks я обновил PrimaryLayout.mocks.ts, чтобы использовать children: '{{component}}' как значение placeholder, чтобы показать в Storybook, куда будет помещен компонент, и я удалил mock props в SidebarLayout. mocks.ts (хотя я не удаляю файл, так что у меня есть готовый интерфейс на случай, если мне понадобится добавить реквизит).

Я также изменил названия историй с templates/... на layouts/....

Наконец, мы можем протестировать его. Сохраните и запустите

yarn dev
Войти в полноэкранный режим Выйдите из полноэкранного режима

Щелкните между двумя маршрутами на боковой панели (Home и About) для переключения между страницами. Обратите внимание, что используемые макеты сохраняются без необходимости перезагрузки (как мы и хотели), и вы получаете супербыстрый и быстрый опыт.

На стороне Storybook мы можем даже просматривать и тестировать компоненты макета независимо от приложения. PrimaryLayout не слишком полезен без содержимого внутри него, но боковая панель очень удобна.

yarn storybook
Вход в полноэкранный режим Выход из полноэкранного режима

Развертывание

Нашим последним шагом будет демонстрация процесса развертывания приложения Next.js.

Мы будем использовать Vercel, поскольку это самое простое и понятное решение для развертывания приложений Next.js (в первую очередь из-за того, что Vercel владеет Next, и поэтому можно предположить, что они будут предлагать лучшую в своем классе поддержку).

Имейте в виду, что Vercel — это не единственный вариант, другие крупные сервисы, такие как AWS, Netlify и т.д., также работают, если вы решите пойти этим путем.

В конечном итоге вам просто нужен сервис, на котором можно выполнить команду next start, предполагая, что вы не используете полностью статически созданный сайт (в этом случае подойдет любой инструмент статического хостинга и не потребуется пользовательский сервер Next).

Развертывание на Vercel в качестве хобби-пользователя совершенно бесплатно. Для начала мы создадим учетную запись на Vercel.

После входа в систему нажмите + New Project и предоставьте Vercel доступ к вашим репозиториям Github. Вы можете предоставить глобальный доступ или выбрать только тот репозиторий, который вы хотите развернуть. Я собираюсь выбрать этот репозиторий под названием nextjs-fullstack-app-template.

После того как вы его выбрали, вам нужно будет его настроить. В разделе Build and Output Settings убедитесь, что вы заменили команды NPM по умолчанию на ваши команды yarn (если вы не используете NPM).

Мы еще не использовали переменные окружения, поэтому добавлять их не нужно.

После этого просто нажмите Deploy и все готово! Это так просто.

(Приведенный выше скриншот немного устарел, я изначально написал раздел развертывания до раздела верстки, но вы поняли идею).

Теперь ваш сайт не только развернут, он будет продолжать автоматически разворачиваться каждый раз, когда вы фиксируете свою основную ветку. Если вы не хотите такого поведения, то его легко настроить в панели Vercel.

Самое приятное, что вы уже настроили команду yarn build для обеспечения работоспособной производственной сборки, прежде чем вы сможете выложить свой код, так что вы можете выкладывать с уверенностью, предполагая, что ваше развертывание будет успешным.

Единственное, о чем вам нужно помнить, это о различиях между вашими двумя окружениями. Все еще возможно, что ваша сборка будет успешной в локальной среде, но неудачной в Vercel, если, например, ваши скрипты отличаются (использование NPM вместо yarn или наоборот) или, что более распространено, если вам не хватает переменных окружения.

Мы будем добавлять значения env в будущих уроках, поэтому вам нужно будет убедиться, что эти значения настроены как в локальной, так и в производственной среде, поскольку они являются секретными и никогда не должны быть зафиксированы в публичном (или даже приватном, если этого можно избежать) репозитории.

Следующие шаги

Я надеюсь, что вы нашли это руководство и узнали кое-что о создании надежного и масштабируемого проекта Next.js для себя и своей команды.

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

Некоторые из моих идей для будущих частей приведены ниже, я бы хотел, чтобы вы оставили свои отзывы о том, какие из них вы считаете наиболее полезными (или о других, если вы не видите их ниже).

  • Как создать полнофункциональное приложение Next.js с использованием маршрутов API и Tailwind CSS
  • Как добавить глобальный менеджер состояний в приложение Next.js с помощью Recoil
  • Как реализовать модульное и сквозное тестирование в приложении Next.s с помощью jest и playwright
  • Как создать CI/CD конвейер с помощью действий Github и Vercel
  • Как реализовать SSO-аутентификацию и интернационализацию в приложении Next.js с помощью NextAuth и i18next
  • Как подключить базу данных к приложению Next.js с помощью Prisma и Supabase
  • Как управлять несколькими приложениями в монорепо с помощью Next.js и Nx

Оставайтесь с нами и, пожалуйста, не стесняйтесь задавать любые вопросы, я буду рад ответить, если смогу!

Подведение итогов

Помните, что весь код из этого руководства как полный пакет доступен в этом репозитории.

Пожалуйста, ознакомьтесь с другими моими учебными пособиями. Не стесняйтесь оставлять комментарии или вопросы и делиться с другими, если вы нашли что-то из них полезным:

  • Как подключить приложение React к базе данных Notion

  • Как использовать Node.js для резервного копирования личных файлов

  • Введение в Docker для Javascript-разработчиков

  • Находки с React Conf 2021

  • Как создать компонент темного режима в React

  • Как анализировать и улучшать производственную сборку ‘Create React App’

  • Как создать и опубликовать библиотеку компонентов React

  • Как использовать IndexedDB для хранения локальных данных в вашем веб-приложении

  • Запуск локального веб-сервера

  • ESLint

  • Красивее

  • Babel

  • React & JSX

  • Webpack: Основы

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *