Перестройка большого веб-сайта электронной коммерции с помощью Next.js (пример из практики)
Опубликовано: 2022-03-10В нашей компании Unplatform мы уже несколько десятилетий создаем сайты электронной коммерции. За эти годы мы видели, как технологический стек эволюционировал от страниц, отображаемых на сервере, с небольшим количеством JavaScript и CSS до полноценных приложений JavaScript.
Платформа, которую мы использовали для наших сайтов электронной коммерции, была основана на ASP.NET, и когда посетители начали ожидать большего взаимодействия, мы добавили React для внешнего интерфейса. Хотя смешивание концепций серверной веб-инфраструктуры, такой как ASP.NET, с клиентской веб-инфраструктурой, такой как React, усложнило ситуацию, мы были вполне довольны решением. Так было до тех пор, пока мы не перешли к производству с нашим клиентом с самым высоким трафиком. С момента выхода в эфир у нас возникли проблемы с производительностью . Core Web Vitals важны, особенно в электронной коммерции. В этом исследовании Deloitte «Миллионы приносят миллионы» исследователи проанализировали данные мобильных сайтов 37 различных брендов. В результате они обнаружили, что улучшение производительности на 0,1 с может привести к увеличению конверсии на 10%.
Чтобы смягчить проблемы с производительностью, нам пришлось добавить много (не предусмотренных бюджетом) дополнительных серверов и активно кэшировать страницы на обратном прокси-сервере. Это даже потребовало от нас отключения некоторых функций сайта. В итоге мы получили очень сложное и дорогое решение, которое в некоторых случаях просто статически обслуживало некоторые страницы.
Очевидно, это казалось неправильным, пока мы не узнали о Next.js. Next.js — это веб-фреймворк на основе React, который позволяет статически генерировать страницы, но вы также можете использовать рендеринг на стороне сервера, что делает его идеальным для электронной коммерции. Его можно разместить на CDN, таком как Vercel или Netlify, что приводит к меньшей задержке . Vercel и Netlify также используют бессерверные функции для рендеринга на стороне сервера, что является наиболее эффективным способом масштабирования.
Проблемы
Разработка с Next.js — это здорово, но есть определенные проблемы. Опыт разработки с Next.js — это то, что вам просто нужно испытать. Код, который вы пишете, мгновенно визуализируется в вашем браузере, а производительность взлетает до небес. Это также риск, потому что вы можете легко слишком сосредоточиться на производительности и пренебречь ремонтопригодностью вашего кода. Со временем это и нетипизированный характер JavaScript могут привести к деградации вашей кодовой базы. Количество ошибок увеличивается, а производительность начинает падать.
Это также может быть сложной задачей во время выполнения . Малейшие изменения в вашем коде могут привести к падению производительности и других важных показателей сети. Кроме того, неосторожное использование рендеринга на стороне сервера может привести к непредвиденным затратам на обслуживание.
Давайте подробнее рассмотрим наши уроки, извлеченные при преодолении этих проблем.
- Модулируйте свою кодовую базу
- Линт и форматируйте свой код
- Используйте машинописный текст
- Планирование производительности и оценка производительности
- Добавьте проверки производительности в ворота качества
- Добавьте автоматические тесты
- Агрессивно управляйте своими зависимостями
- Используйте службу агрегации журналов
- Функция перезаписи Next.js обеспечивает постепенное внедрение
Извлеченный урок: модульность вашей кодовой базы
В наши дни интерфейсные фреймворки, такие как Next.js, позволяют легко начать работу. Вы просто запускаете npx create-next-app и можете начинать программировать. Но если вы не будете осторожны и начнете писать код, не думая о дизайне, вы можете получить большой ком грязи.
Когда вы запустите npx create-next-app
, у вас будет структура папок, подобная следующей (именно так устроено большинство примеров):
/public logo.gif /src /lib /hooks useForm.js /api content.js /components Header.js Layout.js /pages Index.js
Мы начали с той же структуры. У нас было несколько подпапок в папке компонентов для более крупных компонентов, но большинство компонентов находились в корневой папке компонентов. В этом подходе нет ничего плохого, и он подходит для небольших проектов. Однако по мере роста нашего проекта становилось все труднее рассуждать о компонентах и о том, где они используются. Мы даже нашли компоненты, которые вообще больше не использовались! Это также способствует созданию большого кома грязи, потому что нет четких указаний о том, какой код должен зависеть от другого кода.
Чтобы решить эту проблему, мы решили провести рефакторинг кодовой базы и сгруппировать код по функциональным модулям (вроде модулей NPM) вместо технических концепций:
/src /modules /catalog /components productblock.js /checkout /api cartservice.js /components cart.js
В этом небольшом примере есть модуль оформления заказа и модуль каталога. Группировка кода таким образом приводит к лучшей обнаруживаемости: просто взглянув на структуру папок, вы точно знаете, какие функции есть в кодовой базе и где их найти. Кроме того, становится намного проще рассуждать о зависимостях . В предыдущей ситуации между компонентами было много зависимостей. У нас были запросы на внесение изменений в кассу, которые также влияли на компоненты каталога. Это увеличило количество конфликтов слияния и усложнило внесение изменений.
Решение, которое сработало для нас лучше всего, состояло в том, чтобы свести зависимости между модулями к абсолютному минимуму (если вам действительно нужна зависимость, убедитесь, что она однонаправленная) и ввести уровень «проекта», который связывает все вместе:
/src /modules /common /atoms /lib /catalog /components productblock.js /checkout /api cartservice.js /components cart.js /search /project /layout /components /templates productdetail.js cart.js /pages cart.js
Визуальный обзор этого решения:
Уровень проекта содержит код макета сайта электронной коммерции и шаблоны страниц. В Next.js компонент страницы является условным и приводит к физической странице. По нашему опыту, эти страницы часто должны повторно использовать одну и ту же реализацию, поэтому мы ввели понятие «шаблоны страниц». Шаблоны страниц используют компоненты из разных модулей, например, шаблон страницы сведений о продукте будет использовать компоненты из каталога для отображения информации о продукте, а также компонент добавления в корзину из модуля оформления заказа.
У нас также есть общий модуль, потому что есть еще некоторый код, который необходимо повторно использовать функциональными модулями. Он содержит простые атомы, которые являются компонентами React, используемыми для обеспечения согласованного внешнего вида. Он также содержит код инфраструктуры, подумайте об определенных общих ответных хуках или клиентском коде GraphQL.
Предупреждение : убедитесь, что код в общем модуле стабилен, и всегда дважды подумайте, прежде чем добавлять сюда код, чтобы предотвратить запутанный код.
Микроинтерфейсы
В еще более крупных решениях или при работе с разными командами может иметь смысл еще больше разделить приложение на так называемые микроинтерфейсы. Короче говоря, это означает еще большее разделение приложения на несколько физических приложений, которые размещаются независимо на разных URL-адресах. Например: checkout.mydomain.com
и catalog.mydomain.com. Затем они интегрируются другим приложением, которое действует как прокси.
Функциональность перезаписи Next.js отлично подходит для этого, и ее использование поддерживается так называемыми мульти-зонами.
Преимущество нескольких зон заключается в том, что каждая зона управляет своими зависимостями. Это также упрощает постепенное развитие кодовой базы: если выходит новая версия Next.js или React, вы можете обновлять зоны одну за другой вместо того, чтобы обновлять всю кодовую базу сразу. В многокомандной организации это может значительно снизить зависимость между командами.
Дальнейшее чтение
- «Структура проекта Next.js», Янник Виттвер, Medium
- «Руководство 2021 года по гибкому и эффективному структурированию вашего проекта Next.js», Vadorequest, Dev.to.
- «Микрофронтенды», Майкл Джирс
Извлеченный урок: линтинг и форматирование кода
Это то, чему мы научились в более раннем проекте: если вы работаете с одной и той же кодовой базой с несколькими людьми и не используете средство форматирования, ваш код вскоре станет очень непоследовательным. Даже если вы используете соглашения о написании кода и делаете обзоры, вы вскоре начнете замечать разные стили кодирования, которые создают впечатление беспорядочности кода.
Линтер проверит ваш код на наличие потенциальных проблем, а программа форматирования позаботится о том, чтобы код был отформатирован согласованным образом. Мы используем ESLint & prettier и думаем, что они потрясающие. Вам не нужно думать о стиле кодирования, снижая когнитивную нагрузку при разработке.
К счастью, Next.js 11 теперь поддерживает ESLint «из коробки» (https://nextjs.org/blog/next-11), что упрощает настройку путем запуска npx next lint. Это сэкономит вам много времени, поскольку поставляется с конфигурацией по умолчанию для Next.js. Например, он уже настроен с расширением ESLint для React. Более того, он поставляется с новым расширением, специфичным для Next.js, которое даже выявляет проблемы с вашим кодом, которые потенциально могут повлиять на основные веб-жизненные показатели вашего приложения! В следующем абзаце мы поговорим о воротах качества, которые могут помочь вам предотвратить добавление кода в продукт, который случайно нанесет ущерб вашим основным веб-жизненным показателям. Это расширение дает вам обратную связь намного быстрее, что делает его отличным дополнением.
Дальнейшее чтение
- «ESLint», документы Next.js
- «ЭСЛинт», официальный сайт
Извлеченный урок: используйте TypeScript
По мере изменения и рефакторинга компонентов мы заметили, что некоторые свойства компонента больше не используются. Кроме того, в некоторых случаях мы сталкивались с ошибками из-за отсутствующих или неправильных типов свойств, которые передавались компонентам.
TypeScript является надмножеством JavaScript и добавляет типы, что позволяет компилятору статически проверять ваш код, что-то вроде линтера на стероидах.
В начале проекта мы не видели смысла в добавлении TypeScript. Мы чувствовали, что это просто ненужная абстракция. Однако у одного из наших коллег был хороший опыт работы с TypeScript, и он убедил нас попробовать. К счастью, Next.js имеет отличную встроенную поддержку TypeScript, и TypeScript позволяет постепенно добавлять его в решение. Это означает, что вам не нужно переписывать или преобразовывать всю кодовую базу за один раз, но вы можете начать использовать ее прямо сейчас и медленно преобразовывать остальную часть кодовой базы.
Как только мы начали переносить компоненты на TypeScript, мы сразу же обнаружили проблемы с передачей неверных значений в компоненты и функции. Кроме того, цикл обратной связи с разработчиками стал короче, и вы получаете уведомления о проблемах перед запуском приложения в браузере. Еще одно большое преимущество, которое мы обнаружили, заключается в том, что это значительно упрощает рефакторинг кода: легче увидеть, где используется код, и вы сразу же обнаружите неиспользуемые компоненты и код. Вкратце, преимущества TypeScript:
- Уменьшает количество ошибок
- Облегчает рефакторинг вашего кода
- Код становится легче читать
Дальнейшее чтение
- «TypeScript», документы Next.js
- TypeScript, официальный сайт
Извлеченный урок: планируйте производительность и измеряйте производительность
Next.js поддерживает различные типы предварительного рендеринга: статическая генерация и рендеринг на стороне сервера. Для лучшей производительности рекомендуется использовать статическую генерацию, которая происходит во время сборки, но это не всегда возможно. Подумайте о страницах с подробной информацией о продукте, которые содержат информацию о запасах. Такая информация часто меняется, и запуск сборки каждый раз плохо масштабируется. К счастью, Next.js также поддерживает режим, называемый добавочной статической регенерацией (ISR), который по-прежнему генерирует страницу статически, но создает новую в фоновом режиме каждые x секунд. Мы узнали, что эта модель отлично работает для больших приложений. Производительность по-прежнему высока, она требует меньше процессорного времени, чем рендеринг на стороне сервера, и сокращает время сборки: страницы генерируются только по первому запросу. Для каждой добавляемой страницы вы должны думать о необходимом типе рендеринга. Во-первых, посмотрите, можете ли вы использовать статическую генерацию; если нет, используйте добавочную статическую регенерацию, и если это тоже невозможно, вы все равно можете использовать рендеринг на стороне сервера.
Next.js автоматически определяет тип отрисовки на основании отсутствия на странице методов getServerSideProps
и getInitialProps
. Легко сделать ошибку, которая может привести к тому, что страница будет отображаться на сервере, а не генерироваться статически. Вывод сборки Next.js точно показывает, какая страница использует какой тип рендеринга, поэтому обязательно проверьте это. Это также помогает контролировать производство и отслеживать производительность страниц и затрачиваемое процессорное время. Большинство хостинг-провайдеров взимают плату в зависимости от процессорного времени, и это помогает предотвратить неприятные сюрпризы. Я опишу, как мы отслеживаем это, в параграфе «Извлеченный урок: используйте службу агрегации журналов».
Размер пакета
Чтобы иметь хорошую производительность, важно минимизировать размер пакета. Next.js имеет множество готовых функций, которые помогают, например, автоматическое разделение кода. Это обеспечит загрузку только необходимого JavaScript и CSS для каждой страницы. Он также генерирует разные пакеты для клиента и для сервера. Тем не менее, важно следить за ними. Например, если вы неправильно импортируете модули JavaScript, серверный JavaScript может оказаться в клиентском пакете, что значительно увеличит размер клиентского пакета и ухудшит производительность. Добавление зависимостей NPM также может сильно повлиять на размер пакета.
К счастью, Next.js поставляется с анализатором пакетов, который дает вам представление о том, какой код занимает какую часть пакетов.
Дальнейшее чтение
- «Next.js + Webpack Bundle Analyzer», Vercel, GitHub
- «Извлечение данных», документы Next.js
Извлеченный урок: добавьте проверки производительности в ворота качества
Одним из больших преимуществ использования Next.js является возможность статического создания страниц и возможность развертывания приложения на периферии (CDN), что должно привести к отличной производительности и Web Vitals. Мы узнали, что даже с такими отличными технологиями, как Next.js, получить и сохранить отличный балл-маяк очень сложно. Несколько раз случалось, что после того, как мы развернули некоторые изменения в рабочей среде, показатель маяка значительно снизился. Чтобы вернуть контроль, мы добавили автоматические тесты маяков в наши ворота качества. С помощью этого действия Github вы можете автоматически добавлять тесты-маяки в свои запросы на вытягивание. Мы используем Vercel, и каждый раз, когда создается запрос на вытягивание, Vercel развертывает его по URL-адресу предварительного просмотра, и мы используем действие Github для запуска маячных тестов для этого развертывания.
Если вы не хотите настраивать действие GitHub самостоятельно или хотите пойти еще дальше, вы также можете рассмотреть возможность использования сторонней службы мониторинга производительности, такой как DebugBear. Vercel также предлагает функцию аналитики, которая измеряет основные веб-показатели вашего производственного развертывания. Vercel Analytics фактически собирает показатели с устройств ваших посетителей, поэтому эти оценки действительно отражают то, что испытывают ваши посетители. На момент написания Vercel Analytics работает только в производственных развертываниях.
Извлеченный урок: добавьте автоматические тесты
Когда кодовая база становится больше, становится все труднее определить, не нарушили ли ваши изменения кода существующую функциональность. По нашему опыту, очень важно иметь хороший набор сквозных тестов в качестве подстраховки. Даже если у вас небольшой проект, он может сделать вашу жизнь намного проще, если у вас есть хотя бы несколько основных тестов дыма. Мы использовали Cypress для этого и нам очень понравилось. Сочетание использования Netlify или Vercel для автоматического развертывания вашего запроса на извлечение во временной среде и запуска ваших E2E-тестов бесценно.
Мы используем cypress-io/GitHub-action
для автоматического запуска тестов cypress для наших запросов на вытягивание. В зависимости от типа программного обеспечения, которое вы создаете, может быть полезно также иметь более детальные тесты с использованием Enzyme или JEST. Компромисс заключается в том, что они более тесно связаны с вашим кодом и требуют большего обслуживания.
Извлеченный урок: агрессивно управляйте своими зависимостями
Управление зависимостями становится трудоемким, но очень важным занятием при поддержке большой кодовой базы Next.js. NPM упростил добавление пакетов, и кажется, что в наши дни есть пакеты для всего. Оглядываясь назад, можно сказать, что во многих случаях, когда мы добавляли новую ошибку или наблюдалось падение производительности, это было как-то связано с новым или обновленным пакетом NPM.
Поэтому перед установкой пакета вы всегда должны задавать себе следующие вопросы:
- Каково качество упаковки?
- Что будет означать добавление этого пакета для моего размера пакета?
- Этот пакет действительно необходим или есть альтернативы?
- Пакет все еще активно поддерживается?
Чтобы сохранить небольшой размер пакета и свести к минимуму усилия, необходимые для поддержки этих зависимостей, важно, чтобы количество зависимостей было как можно меньше. Ваше будущее «я» поблагодарит вас за это, когда вы будете поддерживать программное обеспечение.
Совет : расширение VSCode Import Cost автоматически показывает размер импортируемых пакетов.
Следите за версиями Next.js
Важно идти в ногу с Next.js и React. Это не только даст вам доступ к новым функциям, но новые версии также будут включать исправления ошибок и исправления потенциальных проблем безопасности. К счастью, Next.js невероятно упрощает обновление, предоставляя Codemods (https://nextjs.org/docs/advanced-features/codemods). Это автоматические преобразования кода, которые автоматически обновляют ваш код.
Обновить зависимости
По той же причине важно поддерживать актуальность версий Next.js и React; также важно обновить другие зависимости. Здесь действительно может помочь зависимый бот Github (https://github.com/dependabot). Он автоматически создаст запросы на вытягивание с обновленными зависимостями. Однако обновление зависимостей может привести к поломке, поэтому автоматические сквозные тесты действительно могут спасти жизнь.
Извлеченный урок: используйте службу агрегации журналов
Мы обнаружили, что абсолютно необходимо настроить службу агрегирования журналов, чтобы убедиться, что приложение работает правильно, и чтобы заранее обнаруживать проблемы. Vercel позволяет вам войти в систему и просматривать журналы, но они передаются в режиме реального времени и не сохраняются. Он также не поддерживает настройку предупреждений и уведомлений.
Некоторые исключения могут занять много времени. Например, мы настроили Stale-While-Revalidate для конкретной страницы. В какой-то момент мы заметили, что страницы не обновляются и обслуживаются старые данные. Проверив журнал Vercel, мы обнаружили, что во время фонового рендеринга страницы возникало исключение. Используя службу агрегации журналов и настроив оповещение об исключениях, мы смогли бы обнаружить это намного раньше.
Службы агрегации журналов также могут быть полезны для отслеживания ограничений тарифных планов Vercel. Страница использования Vercel также дает вам представление об этом, но использование службы агрегации журналов позволяет вам добавлять уведомления при достижении определенного порога. Профилактика лучше, чем лечение, особенно когда речь идет о выставлении счетов.
Vercel предлагает ряд готовых интеграций со службами агрегации журналов, включая Datadog, Logtail, Logalert, Sentry и другие.
Дальнейшее чтение
- «Интеграции», Vercel
Извлеченный урок: функция перезаписи Next.js обеспечивает поэтапное внедрение
Если нет серьезных проблем с текущим веб-сайтом, не многие клиенты будут рады переписывать весь веб-сайт. Но что, если бы вы могли начать с восстановления только тех страниц, которые наиболее важны с точки зрения Web Vitals? Это именно то, что мы сделали для другого клиента. Вместо того, чтобы перестраивать весь сайт, мы перестраиваем только те страницы, которые наиболее важны для SEO и конверсии. В этом случае детали продукта и страницы категорий. Благодаря перестройке с помощью Next.js производительность значительно увеличилась.
Функциональность перезаписи Next.js отлично подходит для этого. Мы создали новый интерфейс Next.js, содержащий страницы каталога, и развернули его в CDN. Все остальные существующие страницы переписываются Next.js на существующий веб-сайт. Таким образом, вы можете начать пользоваться преимуществами сайта Next.js с минимальными усилиями и низким риском.
Дальнейшее чтение
- «Переписывает», документы Next.js
Что дальше?
Когда мы выпустили первую версию проекта и начали серьезное тестирование производительности, мы были в восторге от результатов. Мало того, что время отклика страницы и показатели Web Vitals стали намного лучше, чем раньше, так еще и эксплуатационные расходы стали лишь частью того, что было раньше. Next.js и JAMStack, как правило, позволяют выполнять масштабирование наиболее экономичным способом.
Переход с архитектуры, более ориентированной на серверную часть, на что-то вроде Next.js — большой шаг. Кривая обучения может быть довольно крутой, и поначалу некоторые члены команды действительно чувствовали себя за пределами своей зоны комфорта. Небольшие корректировки, которые мы внесли, уроки, извлеченные из этой статьи, очень помогли в этом. Кроме того, опыт разработки с Next.js дает потрясающий прирост производительности. Цикл обратной связи с разработчиками невероятно короток!
Дальнейшее чтение
- «Переход к производству», Next.js Docs