Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
![image](https://habrastorage.org/webt/j7/mv/w8/j7mvw8t8i4ycf0siv-8sjjiixte.jpeg)
Сети доставки контента
Когда вы переводите свой сайт в эксплуатацию, статические ресурсы должны быть выложены где-то в Интернете. Возможно, вы привыкли выкладывать их на том же сервере, где генерируется весь ваш динамический HTML. В нашем примере до сих пор также использовался данный подход: запускаемый командой node meadowlark.js сервер Node/Express раздает как все виды HTML, так и статические ресурсы. Однако, если хотите оптимизировать производительность вашего сайта (или заложить эту возможность на будущее), вам понадобится возможность выкладывать статические ресурсы в сети доставки контента (content delivery network, CDN). CDN — сервер, оптимизированный для доставки статических ресурсов. Он использует специальные заголовки (о которых мы скоро узнаем больше), включающие кэширование в браузере.
Помимо этого, CDN может включать географическую оптимизацию (часто называемую edge caching), то есть статическое содержимое будет доставляться с ближайшего к вашему клиенту сервера. Хотя Интернет очень быстр (конечно, он работает не со скоростью света, но с близкой к ней), данные будут доставляться еще быстрее с расстояния сотни километров, чем тысячи. Экономия времени в каждом отдельном случае незначительна, но, если умножить ее на количество пользователей, запросов и ресурсов, она быстро приобретет внушительные размеры.
На большую часть ваших статических ресурсов будут ссылаться в представлениях HTML
элементы <link> ссылаются на CSS-файлы, <script> — на файлы JavaScript, теги <img> будут ссылаться на изображения, также имеются теги внедрения мультимедийных файлов
Широко распространена практика статических ссылок в CSS, обычно в свойстве background-image. И наконец, на статические ресурсы иногда ссылаются в JavaScript, например, код JavaScript может динамически менять или вставлять теги
<img>
или свойства background-image.
Не стоит беспокоиться о совместном использовании ресурсов разными доменами (CORS) при применении CDN. Загружаемые в HTML внешние ресурсы не подчиняются правилам CORS: вам требуется активизировать CORS только для ресурсов, загружаемых через Ajax (см. главу 15).
Проектирование для CDN
То, как вы будете использовать CDN, зависит от архитектуры вашего сайта. Большинство сетей доставки контента позволяют устанавливать правила маршрутизации, чтобы определить, куда отправлять входящие запросы. Хотя вы можете установить довольно сложные правила маршрутизации, обычно все сводится к отправке запросов статических ресурсов в одно место (как правило, предоставляемое вашей CDN) и запросов динамических конечных точек (динамических страниц или конечных точек API) — в другое.
Выбор и конфигурация CDN — обширная тема, которую я не буду здесь раскрывать, но дам базовые сведения, что поможет настроить выбранную вами CDN.
Наиболее простой подход к созданию структуры вашего приложения — сделать динамические и статические ресурсы легко различимыми. Это позволит максимально упростить правила маршрутизации CDN. Хотя этого можно достичь с помощью поддоменов (например, динамические ресурсы выдаются с meadowlark.com, а статические — с static.meadowlark.com), данный подход связан с дополнительными трудностями и усложняет локальную разработку. Более простой способ — использование путей запросов: например, все, что начинается с /public/, — это статические ресурсы, а все остальное — динамические. Подход может отличаться, если вы генерируете содержимое с помощью Express или используете Express с целью предоставления API для одностраничного приложения.
Веб-сайт с рендерингом на стороне сервера
Если вы используете Express для рендеринга динамического HTML (проще говоря, все, что начинается со /static/), это статические ресурсы, а все остальное —динамические. При таком подходе все ваши (динамически генерируемые) URL будут такими, какими вы хотите их видеть (если только они не начинаются со /static/, конечно же!), а все ваши статические ресурсы будут иметь префикс /static/:
<img src="/static/img/meadowlark-logo-1.png" alt="Meadowlark Logo">
Добро пожаловать в <a href="/about">Meadowlark Travel</a>.
До сих пор в этой книге мы использовали промежуточное ПО static, как если бы все статические ресурсы выкладывались в корневом каталоге. Таким образом, помещая статический ресурс foo.png в папку public, мы ссылаемся на него по пути URL /foo.png, а не /static/foo.png. Конечно, можно создать подкаталог static внутри существующего каталога public, и URL для /public/static/foo.png будет /static/foo.png, но это не очень разумно. К счастью, промежуточное ПО static позволяет нам избежать этого — достаточно указать другой путь при вызове app.use:
app.use('/static', express.static('public'))
Теперь в среде разработки мы можем использовать ту же структуру URL, что и при эксплуатации. Если содержимое папки public и CDN синхронизировано, можно ссылаться на статические ресурсы в обоих местах и без проблем переключаться между разработкой и эксплуатацией.
При настройке маршрутизации для CDN (вам нужно будет обратиться к документации по вашей CDN) маршрутизация будет выглядеть следующим образом.
![image](https://habrastorage.org/webt/83/ox/oa/83oxoa3wcbygozch9lu_inxgayc.png)
Одностраничные приложения
Одностраничные приложения, как правило, являются противоположностью веб-сайтов с рендерингом на стороне сервера: серверу будет передаваться только API (например, любой запрос, начинающийся с /api), все остальное передается в статическое файловое хранилище.
В главе 16 вы увидели, что можно создать сборку для эксплуатации вашего приложения, в которую войдут все статические ресурсы, загружаемые в CDN. Затем нужно будет только удостовериться, что настройка маршрутизации вашего API корректна. Таким образом, у вас будет следующая маршрутизация.
![image](https://habrastorage.org/webt/r8/md/y5/r8mdy5cyib3gyk5xcwziggfat18.png)
Узнав, как можно структурировать приложение, чтобы без проблем перейти от разработки к эксплуатации, обратимся к тому, что на самом деле происходит при кэшировании и как это оптимизирует производительность.
Кэширование статических ресурсов
Независимо от того, что вы используете для раздачи статических ресурсов — Express или CDN, стоит разобраться в заголовках HTTP-запросов, которые браузер использует для определения, когда и как кэшировать статические ресурсы.
— Expires/Cache-Control. Эти два заголовка информируют ваш браузер о максимальном количестве времени, в течение которого ресурс может храниться в кэше. Браузер воспринимает их всерьез: если они приказывают ему хранить что-либо в течение месяца, он попросту не станет загружать это заново на протяжении месяца, до тех пор пока оно остается в кэше. Важно понимать, что браузер может удалить изображение из кэша до истечения срока по причинам, которые вы не в состоянии контролировать. Например, пользователь может очистить кэш вручную или браузер может удалить ваш ресурс, чтобы освободить место для чаще посещаемых пользователем ресурсов. Вам необходим только один из этих заголовков. Expires поддерживается более широко, так что предпочтительнее использовать его. Если ресурс находится в кэше и срок его хранения еще не истек, браузер вообще не выполнит запрос GET, что улучшит производительность, особенно на мобильных устройствах.
— Last-Modified/ETag. Обеспечивают своего рода контроль версий: если браузеру необходимо извлечь ресурс, он проверит эти теги до загрузки содержимого. Запрос GET к серверу все же будет выполнен, но, если возвращаемые в этих заголовках значения продемонстрируют браузеру, что ресурс не менялся, к загрузке файла браузер не перейдет. Как можно догадаться по имени, Last-Modified позволяет вам задать дату последнего изменения ресурса. А ETag дает возможность использовать произвольную строку, обычно строку с версией или хеш содержимого.
При выдаче статических ресурсов следует использовать заголовок Expires и либо Last-Modified, либо ETag. Встроенное в Express промежуточное ПО static устанавливает Cache-Control, но не обрабатывает ни Last-Modified, ни ETag. Поэтому для разработки оно подходит, а для эксплуатации — нет.
Если вы решили выкладывать свои статические ресурсы в CDN, такие как Amazon CloudFront, Microsoft Azure, Fastly, Cloudflare, Akamai или StackPath, то получите бонус: большинство таких деталей будут обработаны за вас. Вы сможете произвести точную настройку, хотя настройки по умолчанию в любом из этих сервисов также хороши.
Изменение статического содержимого
Кэширование значительно улучшает производительность вашего сайта, но не без последствий. В частности, при изменении любого из статических ресурсов клиенты могут не увидеть изменений до тех пор, пока не истечет срок хранения закэшированных версий в браузере. Google рекомендует кэшировать на срок один месяц, а лучше — один год. Представьте пользователя, который заходит на ваш сайт каждый день через один и тот же браузер: он может увидеть ваши обновления только через год!
Очевидно, что такая ситуация нежелательна, но вы не можете приказать своим пользователям очистить кэш. Решение этой проблемы заключается в запрете кэширования (cache busting). Этот прием даст вам контроль над тем, когда браузер должен перезагружать ресурс. При этом методе к имени файла просто добавляется какая-либо информация о его версии. Когда вы обновляете ресурс, имя ресурса меняется и браузер узнает о необходимости его скачать. Как правило, это равносильно контролю версий ресурса (main.2.css или main.css?version=2) или добавлению какого-нибудь хеша (main.e16b7e149dccfcc399e025e0c454bf77.css). Какой бы метод вы ни использовали, при обновлении ресурса его название меняется и браузер знает, что его нужно загрузить.
С мультимедийными ресурсами можно поступить аналогично. Возьмем наш логотип (/static/img/meadowlark_logo.png). Если мы выкладываем его в CDN для максимального увеличения производительности, задавая длительность периода хранения один год, а затем меняем логотип, пользователи могут не увидеть изменений на протяжении года. Однако, если мы переименуем логотип в /static/img/meadowlark_logo-1.png и отразим это изменение в HTML, браузеру придется скачать его, поскольку он кажется новым ресурсом.
Если вы остановились на фреймворке одностраничных приложений, таком как create-react-app или аналогичном, то на этапе построения будет создана сборка готовых к эксплуатации ресурсов с добавлением к ним хешей.
Если вы начинаете с нуля, то, вероятно, захотите взглянуть на сборщики (это то, что находится под капотом фреймворков одностраничных приложений). JavaScript, CSS и некоторые другие типы статических ресурсов будут объединены в как можно меньшее количество сборок, и результат будет предельно минимизирован. Настройка сборки — обширная тема, но, к счастью, по ней есть много хорошей документации. Ниже приведены наиболее популярные сборщики, доступные на данный момент.
— Webpack (https://webpack.js.org/). Один из первых сборщиков, достигших настоящего подъема. У него до сих пор много сторонников. Он очень сложный, и за эту сложность приходится платить: кривая обучаемости крутая. Однако данный упаковщик хорош для обучения азам.
— Parcel (https://parceljs.org/). Появился недавно и наделал много шума. Он чрезвычайно хорошо задокументирован, очень быстрый, и, главное, у него самая короткая кривая обучаемости. Он подходит, если нужно сделать работу быстро и без хлопот.
— Rollup (https://rollupjs.org/). Находится где-то между Webpack и Parcel. Как Webpack, он надежен и многофункционален. Однако начать работать с ним проще, чем с Webpack, но не так легко, как с Parcel.
Резюме
При всей кажущейся простоте статические ресурсы доставляют массу хлопот. Однако они составляют основную массу передаваемых вашим посетителям данных, так что потраченное на их оптимизацию время окупится с лихвой.
Действенное решение для статических ресурсов, не упоминавшееся ранее, — выложить статические ресурсы в CDN и всегда использовать в представлениях и CSS полный URL к ресурсу. У этого подхода есть преимущество: он очень прост, но, если когда-нибудь вы пожелаете провести недельный хакатон в лесной хижине, где нет доступа к Интернету, у вас будут проблемы!
Тщательно продуманные сборка и минимизация — еще одна сфера, в которой вы можете сэкономить время, если для вашего приложения выигрыш в производительности не оправдывает приложенных усилий. В частности, если на вашем сайте только один или два JavaScript-файла, а все CSS находятся в одном файле, можно вообще отказаться от сборки, но реальные приложения имеют тенденцию расти со временем.
Какой бы метод вы ни выбрали для раздачи статических ресурсов, я советую выкладывать их отдельно, лучше всего в CDN. Если вам кажется, что это хлопотно, уверяю: это совсем не так сложно, как кажется. Особенно если вы предварительно потратите немного времени на систему развертывания так, что развертывание статических ресурсов в одно местоположение, а приложения — в другое будет автоматическим.
Если вас беспокоит стоимость хостинга в CDN, призываю взглянуть на суммы, которые вы сейчас платите за хостинг. Большинство провайдеров хостинга берут большие деньги за трафик, даже если вы об этом не знаете. Однако, если внезапно ваш сайт оказался упомянут на Slashdot и вы испытали на себе слешдот-эффект, вам может прийти совершенно неожиданный счет за услуги хостинга. CDN-хостинг обычно настраивается таким образом, что вы платите только за то, что используете. Приведу пример: сайт для местной компании средних размеров, которым я управляю, использует примерно 20 Гбайт трафика в месяц, при этом плата за размещение статических ресурсов (а это весьма насыщенный медиафайлами сайт) составляет лишь пару долларов в месяц.
Получаемые за счет выкладывания статических ресурсов в CDN выгоды существенны, а стоимость и неудобства от этого минимальны, так что я решительно советую вам выбрать этот путь.
С помощью определенных трюков JavaScript в браузере можно использовать некомпилированный LESS. Однако такой подход может привести к негативным последствиям в плане производительности, так что я не советую его применять.
Более подробно с книгой можно ознакомиться на сайте издательства
» Оглавление
» Отрывок
Для Хаброжителей скидка 25% по купону — JavaScript
По факту оплаты бумажной версии книги на e-mail высылается электронная книга.