Мой любимый некоммерческий сайт получил новый вид и повысил производительность

Моя большая вещь сейчас - это CMS без заголовка - и с помощью API REST Wordpress, недавно включенного в ядро, вполне возможно отказаться от тем Wordpress и преобразовать устаревший сайт Wordpress, созданный с помощью HTML, CSS и PHP, в одностраничное приложение (SPA). используя последние достижения JavaScript. Одна из основных проблем, которую необходимо решить, - как сохранить SEO. Хотя Google начал уделять внимание индексации SPA, это еще не на должном уровне для среднего веб-сайта электронной коммерции с потенциально тысячами сообщений и продуктов. Next.js, минималистичный фреймворк от сотрудников Zeit, решает эту проблему и многое другое!

Зачем обходиться без головы?

Прежде чем я перейду к техническому обсуждению того, как я это создал, позвольте мне задать основной вопрос: зачем кому-то создавать «безголовый» веб-сайт?

«Безголовый» сайт в основном означает, что интерфейс отделен от серверной части, а серверная часть (в данном случае Wordpress) обслуживает контент через API, а не обрабатывает логику внешнего интерфейса. В настройке без головы вы в основном отказываетесь от встроенного движка тем и используете свою собственную технологию для обработки сборки.

Позвольте мне выступить в роли защитника дьявола против моей собственной идеи: с Wordpress вы получаете много бесплатных вещей, которые вы не получите в автономной / несвязанной настройке: предварительно созданные темы, плагины для интерфейсных функций, целую экосистему решения для решения проблем, так как родной Wordpress существует уже давно. Безголовые сайты - относительно новая территория, и, следовательно, все, что вы получаете бесплатно с Wordpress, просто улетучивается. Виджет поиска? Забудьте об этом, вам придется кататься самостоятельно.

Звучит ужасно, но на самом деле это означает, что ваш сайт будет намного легче, чем средний сайт Wordpress, и вы можете использовать любую технологию, которую хотите создать. Организация сайта React или Angular также лучше, чем у сайта Wordpress на основе PHP. Вы получите лучшее разделение проблем и, в конечном итоге, большую безопасность, поскольку серверная часть взаимодействует только через API и не обслуживает интерфейсную часть.

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

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

Универсальные веб-приложения

Как бы вы это ни называли, JavaScript, отображаемый на сервере, был сложной проблемой для многих разработчиков. С PHP это не было чем-то, о чем мы должны были думать - потому что это происходит по умолчанию. Помимо фактического рендеринга частичных файлов на стороне сервера с использованием чего-то вроде EJS, React - ваш лучший выбор для рендеринга на стороне сервера «из коробки». Вы даже не хотите смотреть на Angular 2/4/6/10 (или что-то еще) Universal…

В предыдущей версии Dine-N-Dash, веб-сайта для мероприятий, который я создал ранее в этом году, я использовал React.renderToString для рендеринга статического контента на сервере в виде красивого HTML - несложная вещь. делать. Однако есть одна большая проблема: как насчет асинхронного режима? А как насчет всего этого контента, полученного из Wordpress? Без какой-либо сложной проводки (Redux на сервере?) Сервер не будет отображать какой-либо контент в формате HTML. У вас остается только тот жестко запрограммированный контент, который есть в ваших компонентах, и ничего динамического не отображается.

Next.js очень красиво решает эту проблему.

Функция getInitialProps - это, по сути, ваш билет на асинхронную визуализацию извлеченного контента на сервере до того, как он попадет на клиент. Вы увидите здесь и другие забавные вещи, такие как Async / Await, и в нем используется изоморфная выборка, чтобы эти вызовы стали меньше и легче читались. У меня есть некоторые из моих компонентов, выполняющих три разных запроса на сервере перед рендерингом, и это все еще не кажется медленным!

Возвращаются новые реквизиты… в данном случае this.props.page и this.props.recentPosts для одного из моих одностраничных шаблонов. Вы также заметите, что он принимает параметр запроса для параметризованных маршрутов. Я использую библиотеку Next-Routes, чтобы сделать это немного проще, но я верю, что есть способ сделать это из коробки.

Беспрецедентная простота

Что действительно круто в Next, так это то, что он обрабатывает весь мусор, с которым вам обычно приходится иметь дело при создании веб-сайта React - Webpack, Hot Module Reloading (HMR), маршрутизацию, сервер (в основном). По умолчанию у вас может быть просто две папки: страницы и компоненты, страницы - это ваши маршруты, а компоненты - все остальное, а все остальное он сделает за вас! Для очень простого сайта даже не нужен сервер. В моем случае настраиваемый сервер выглядит так, потому что мне нужен настраиваемый порт и использование параметризованных маршрутов ...

Это все. Ничего особенного. Даже моя бабушка смогла создать сайт Next…

Я придумал один трюк, и я как сумасшедший взламывал производительность на этом сайте, поэтому, очевидно, я включил сжатие gzip. Хотя текстовые ресурсы / скрипты, связанные со страницей, будут сжиматься, сама страница не будет сжата, поскольку она SSRed и на самом деле не имеет типа содержимого - вам нужно добавить заголовок 'text / html', чтобы NGINX распознал и сжать его. Легко глупо.

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

Точно так же, если вы используете Nodemon или что-то подобное при разработке для сервера, он не будет работать с Next, поскольку у него есть собственная обработка HMR. Просто держите сервер Next.js чистым и предназначенным для одной цели и сделайте отдельный сервер Node для всего остального (если вам нужно - вы можете и не делать!). Используйте сценарий NPM для одновременного запуска обоих.

NPM RUN DEV (для разработки с HMR и с настраиваемым сервером…):

"dev": "node server.js & nodemon ./api_server/server.js

НПМ СТАРТ (для производства):

"start": "NODE_ENV=production node ./server.js & NODE_ENV=production node ./api_server/server.js",

Страницы и компоненты в следующем

Все мои страницы заключены в компонент Layout, который также содержит метатеги, отображаемые на каждой странице, и включает такие вещи, как навигация, которая используется на каждой странице. Это также из коробки с Next: возможность устанавливать метатеги с помощью компонента Head ... Моя домашняя страница выглядит так (без импорта для краткости), и я добавляю некоторые реквизиты этому компоненту Layout для метатегов, если вам было любопытно ...

По большей части это стандартный React. Next - это очень минималистичный фреймворк, и то, что он обрабатывает, делает React рутинной работой. Он позволяет React быть React и заботится обо всем остальном.

Стилизованный JSX

Next поддерживает множество различных способов создания CSS, и, как мы знаем, существуют самые разные мнения о препроцессорах. Честно говоря, сейчас в этом пространстве много усталости от принятия решений. Хотя я обычно предпочитаю Sass, я решил попробовать пакетный метод обработки CSS, Styled JSX - еще один замечательный инструмент от Zeit peeps, который поставляется с Next, вам даже не нужно устанавливать его NPM.

Styled JSX - это знакомый встроенный «CSS в JavaScript», к которому вы привыкли с React, позволяющий иметь локальные стили, которые не каскадируются, но обрабатывает их рендеринг на стороне сервера. Супер здорово! Даже с отключенным JavaScript ваш сайт будет в основном нетронутым при первоначальной визуализации ... Он также автоматически добавляет множество префиксов поставщика браузера, поэтому вам не нужно о них думать.

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

Время рендеринга CSS на самом деле является огромной проблемой, если вы используете что-то вроде Bootstrap, чего я здесь избегал. Единственное, что я использовал, это сетка Bootstrap 4 (просто сетка), к которой я привык - я вытащил отдельную сетку и удалил практически все стили, которые не использую. Это было просто удобство. Я включил сетку как встроенный CSS в компонент макета. Он отлично работает ... В будущем я буду использовать очень новый стандарт CSS Grid, чтобы вообще не возиться с этим.

Одна вещь, которую я заметил с встроенным CSS, так как это был первый раз, когда я его использовал, это то, что он иногда не работает должным образом с некоторым контентом, например, что установлено с помощью «опасноSetInnerHtml», что очень важно для использования, поскольку разметка Wordpress хранится как HTML. Наличие встроенных стилей может привести к некоторой противоречивой визуализации, поскольку этот контент не является частью тех же узлов DOM, что и родительский компонент (по крайней мере, моя теория). Стилизованный JSX предоставляет «глобальный» вариант, в котором вы можете встраивать такие вещи, как ваша сетка, шрифты, стили галереи Wordpress и, в основном, вещи, которые вам понадобятся на каждой странице - это решает большинство проблем с встраиванием, поэтому он действует как обычный CSS.

Причудливый Wordpress REST API

Некоторые могут спросить, почему Wordpress - почему не Contentful или Prismic или один из новых крутых, действительно безголовых детей на блоке (lol meme, это, пожалуйста)? Что ж, когда вы имеете дело с сайтом 5-летней давности, вам нужно перенести много старого контента. Может быть, мы в конце концов доберемся до этого ... Wordpress делает то, что нам нужно, по большей части, и это бесплатно, а мои конечные пользователи не разработчики (они занимаются сбором средств / общаются), поэтому они знакомы с Wordpress.

В этом году Wordpress интегрировал REST API в ядро, и с помощью нескольких плагинов вы можете делать с ним практически все, что захотите. Дьявол кроется в деталях. По иронии судьбы, самой большой проблемой здесь было «проверенное и верное», а не дружелюбный и симпатичный Next.js.

Эффективная обработка маршрутов и сохранение старых URL-адресов в неприкосновенности

Одна из интересных проблем Wordpress заключается в том, что REST API не предлагает тех же фокусов, что и собственный PHP API. Поэтому для большинства из нас, если у нас есть страница по адресу mysite.com/about и сообщение по адресу mysite.com/todays-news, Wordpress внутренне выбирает, какой шаблон использовать в зависимости от типа сообщения. Но с REST API вы не получите такого волшебства. Вы должны знать заранее, пост это или страница. Следовательно, параметризованный маршрут с mysite.com/:slug не скажет вам, какую конечную точку API выбрать.

Введите плагин WP Multiple Post Type, который позволяет вам запрашивать API для любого предварительно настроенного контента, который вы выбираете в одном запросе. Промежуточное ПО Express для моего маршрута сообщений / страниц все-в-одном приведено ниже. Обратите внимание, что здесь я использую Redis и Bluebird Promises, я расскажу об этом чуть позже. Затем во внешнем интерфейсе я могу отображать такие вещи, как дата или подзаголовок, в зависимости от типа сообщения.

Обратите внимание на класс «new PostOrPage (content.body [0])», в котором я фильтрую и управляю ответом для внешнего интерфейса, прежде чем отправить его. С SSR ваш асинхронный код не просто хранится в памяти… весь объект возвращается как статически визуализированное содержимое скрипта. Вы не хотите возвращать кучу мусора в интерфейс, который вы не используете, так как это увеличит размер загружаемой страницы ... Это то, что я называю делом любви.

Избранные изображения

На самом деле избранные изображения были большой проблемой с REST API. Они не только глубоко вложены в кластер объекта / массива, но и в зависимости от того, что загружает пользователь, изображение функции может существовать, а может и не существовать ...

Проблема возникает на стороне сервера. Все мои REST-маршруты Wordpress обслуживаются промежуточным сервером Express. Это нужно для того, чтобы я мог кэшировать их в Redis, но также и для того, чтобы сократить ответ, чтобы он не был таким подробным. Я настраиваю класс ES6, который используется для фильтрации очень подробного ответа от Wordpress, чтобы отправить что-то, с чем проще работать во внешнем интерфейсе. Думаю, теперь я ценю GraphQL - лол.

Проблема: если пользователь загружает миниатюрное изображение, меньшее, чем размер кадрирования, Wordpress не возвращает вместо него «нулевой» ответ или какой-либо вариант по умолчанию. Объект - и даже массив - просто не существует. Когда вы имеете дело с массивами, вложенными в объекты, это может привести к катастрофической ошибке, когда запрос полностью завершится ошибкой по сравнению с возвратом нуля для конкретного поля. Так что сначала вам нужно проверить это. Вот это боль.

Здесь также мне пришлось сделать немного PHP, чтобы добавить новые избранные изображения в файл Functions.php ...

Расширенные настраиваемые поля

Расширенные настраиваемые поля - это магия, которая делает Wordpress в основном CMS для любого типа веб-сайтов, а не только движком для ведения блогов. Обычно я использую расширенные настраиваемые поля и настраиваемые типы сообщений по всему сайту - от Chef Network Bios до всей категории Проекты… Даже изображения слайдеров реализованы как настраиваемые поля. По сути, это должно быть возвращено REST API. Есть очень простой плагин, который сделает это за вас. По иронии судьбы, с содержимым настраиваемого поля гораздо проще работать, чем с собственным содержимым Wordpress.

SEO / Yoast

Первоначально мы использовали All-In-One SEO Pack для обработки таких вещей, как метатеги и изображения для публикации в социальных сетях на сайте, но эти поля не возвращаются в REST API. Я переключил нас на Yoast, у которого есть способ конвертировать весь этот контент, и есть хороший плагин, который вы можете загрузить, чтобы открыть поля для REST API!

Обработка сквозняков

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

Архивы и поиск

У REST API очень мало архивов. Архивы по дате особенно проблематичны, вам в основном приходится создавать собственные с отдельными выборками и действием цикла.

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

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

Посетите страницу Истории, чтобы увидеть все это в действии…

Кеширование

REST API Wordpress работает медленно, особенно если у вас много плагинов. Каждый плагин должен быть инициализирован во время ответа. Как упоминалось ранее, я решил использовать Redis, чтобы решить некоторые из этих проблем, кэшируя ответы API. С Redis я могу уменьшить время отклика 200–500 мс до примерно 30–40 мс в кэше. Это очень важно и стоит дополнительных усилий.

Я попробовал один из плагинов Redis для Wordpress, и мне показалось, что он не ускоряет ответ REST API, поэтому я решил применить свой собственный.

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

Войдите в файл Functions.php, в котором я написал очень простой запрос CURL, чтобы поразить мою конечную точку с удалением.

Эта функция срабатывает довольно надежно при сохранении страницы или сообщения и сбрасывает кеш.

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

Если я буду внимательнее относиться к тому, как хранятся эти объекты, возможно, я смогу заменить, а не сбрасывать то, что находится в кеше, обновленным содержимым. Но для правильного выполнения этого требуется много PHP (чего я в основном стараюсь избегать во всей этой сборке!). Кэширование может быть очень сложным и, вероятно, не стоит такого уровня детализации ... Так что это скорее быстрый и грязный кеш.

Кеширование стало меньшей проблемой, когда я перешел в производственную среду, очистил целую кучу плагинов, включил gzip и HTTP2 - тогда я постоянно получал

Результаты производительности

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

С этим веб-сайтом я потратил около 70 процентов своего времени на оптимизацию производительности, где у меня был контроль. Я удалил каждый бит CSS и JavaScript, который мог удалить. Я использую инструмент Lighthouse от Google (теперь часть DevTools) для измерения производительности. Старая домашняя страница постоянно имела рейтинг 45–60. С новым дизайном я получил вот это:

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

Наибольшее влияние на производительность оказали включение gzip в NGINX, создание функций с нуля вместо использования готовых библиотек, сжатие изображений (например, заголовок домашней страницы), встраивание CSS и предоставление различных изображений для мобильных устройств и компьютеров, где я мог.

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

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

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

Выводы и результаты

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

У меня не было возможности поиграть с некоторыми функциями Next 3.0 (они были выпущены только вчера!), Такими как экспорт статических сайтов - я уверен, что это действительно здорово! Я действительно заметил, что между версией 2.4.9, на которой я первоначально запустил этот сайт, и версии 3.0 - произошло заметное увеличение производительности при разработке и производстве. Престижность команде Zeit за создание такой прочной структуры, которая улучшит работу наших сайтов React!

Вам действительно стоит заглянуть на сайт и в организацию World Central Kitchen. Это отличная группа людей, которых я знаю уже много лет - они используют силу еды, социальное предпринимательство и сеть талантливых, а иногда и известных поваров, чтобы изменить жизнь во всем мире. Какая классная миссия! Кроме того, не пропустите Dine-N-Dash в июне следующего года - если вы будете в Вашингтоне, округ Колумбия. Это веселое мероприятие. Этот сайт также создан с помощью Next. Я здесь 2 из 2! Спасибо за прочтение.