Перейти к содержимому

Как кэшировать js библиотеку при импорте

  • автор:

Как кэшировать js библиотеку при импорте

Это руководство раскрывает файлопровод (asset pipeline).

Обратившись к этому руководству, вы узнаете:

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

1. Что такое файлопровод (Asset Pipeline)?

Файлопровод представляет фреймворк для обработки доставки ассетов JavaScript и CSS. Это выполняется с использованием технологий, таких как HTTP/2, и техник, таких как конкатенация и минификация. Наконец, это позволяет приложению автоматически соединяться с ассетами других гемов.

Файлопровод реализован в гемах importmap-rails, sprockets и sprockets-rails и включен по умолчанию. Можно отключить файлопровод при создании нового приложения, передав опцию —skip-asset-pipeline .

$ rails new appname --skip-asset-pipeline 

Это руководство фокусируется на файлопроводе по умолчанию с использованием только sprockets для обработки CSS и importmap-rails для JavaScript. Главное ограничение у них в том, что нет поддержки для транспиляции, поэтому нельзя использовать такие вещи как Babel , Typescript , Sass , React JSX format или TailwindCSS . Мы рекомендуем прочитать раздел про альтернативные библиотеки, если вам необходима транспиляция для JavaScript/CSS.

2. Основные особенности

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

Второй особенностью файлопровода является использование карт импорта при раздаче файлов JavaScript. Это позволяет создавать современное приложение с помощью библиотек Javascript, сделанных для модулей ES (ESM), без необходимости транспиляции и сборки. В свою очередь, это устраняет необходимость Webpack, yarn, node или любой другой части инструментария JavaScript.

Третьей особенностью файлопровода является соединение всех CSS файлов в один главный файл .css , который затем минифицируется или сжимается. Как будет сказано далее в этом руководстве, можно настроить эту стратегию, сгруппировав файлы любым способом. В production, Rails вставляет метку SHA256 в каждое имя файла, таким образом файл кэшируется браузером. Кэш можно сделать недействительным, изменив эту метку, что происходит автоматически каждый раз, когда изменяется содержимое файла.

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

2.1. Что за метки и зачем они нужны?

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

Когда имя файла уникально и основано на его содержимом, заголовками HTTP можно установить повсеместное кэширование (в CDN, у провайдера, в сетевом оборудовании или браузере), чтобы у них была собственная копия содержимого. Когда содержимое изменяется, метка тоже изменится. Это приведет к тому, что удаленные клиенты запросят новую копию содержимого. Эта техника известна как cache busting.

Техникой, используемой Sprockets для меток, является вставка хэша содержимого в имя, обычно в конце. Например, файл CSS global.css :

global-908e25f4bf641868d8683022a5b62f54.css 

Это стратегия, принятая файлопроводом Rails.

Метки включены по умолчанию для сред development и production. Их также можно включить или отключить в конфигурации с помощью опции config.assets.digest .

2.2. Что такое карты импорта (Import Maps), и зачем они мне?

Карты импорта позволяют импортировать модули JavaScript с помощью логичных имен, связанных с версионированными/мечеными файлами, – прямо из браузера. Таким образом, можно создавать современное приложение с помощью библиотек Javascript, сделанных для модулей ES (ESM), без необходимости транспиляции и сборки.

С таким подходом можно поставлять множество маленьких файлов JavaScript вместо одного большого файла JavaScript. Это благодаря HTTP/2, который больше не несет ухудшения материального быстродействия во время первоначальной доставки, а фактически даже предлагает значительные преимущества в долгосрочной перспективе за счет лучшей динамики кэширования.

3. Как использовать карты импорта в качестве файлопровода Javascript

Карты импорта являются обработчиком Javascript по умолчанию, логика генерации карт импорта обрабатывается гемом importmap-rails .

Карты импорта используются только для файлов Javascript, и не могут быть использованы для доставки CSS. Чтобы узнать о CSS, обратитесь к разделу по Sprockets.

Детальные инструкции по использованию находятся на домашней странице гема, но важно понимать основы importmap-rails .

3.1. Как они работают

Карты импорта по сути это строковая замена для того, что называется «bare module specifiers». Они позволяют стандартизировать имена модулей импорта JavaScript.

Возьмем, к примеру, такое определение импорта, оно не будет работать без карты импорта:

import React from "react" 

Вы бы могли определить его так, чтобы оно заработало:

import React from "https://ga.jspm.io/npm:react@17.0.2/index.js" 

Тут вступает карта импорта, мы определяем, что имя react прикреплено к адресу https://ga.jspm.io/npm:react@17.0.2/index.js . С такой информацией браузер принимает упрощенное определение import React from «react» . Можно думать о карте импорта как о псевдониме для адреса исходника библиотеки.

3.2. Использование

С помощью importmap-rails создается файл конфигурации importmap, привязывая путь библиотеки к имени:

# config/importmap.rb pin "application" pin "react", to: "https://ga.jspm.io/npm:react@17.0.2/index.js" 

Все сконфигурированные карты импорта должны быть прикреплены к элементу приложения, добавив . javascript_importmap_tags рендерит ряд скриптов в элемент head :

  • JSON со всеми сконфигурированными картами импорта:
 type="importmap">  "imports":  "application": "/assets/application-39f16dc3f3. js" "react": "https://ga.jspm.io/npm:react@17.0.2/index.js" > >  
  • Es-module-shims , действующий как полифил, обеспечивающий поддержку для import maps в старых браузерах:
 src="/assets/es-module-shims.min" async="async" data-turbo-track="reload"> 
  • Точку входа для загрузки JavaScript из app/javascript/application.js :
 type="module">import "application"  
3.3. Использование пакетов npm из JavaScript CDN

Можно использовать команду ./bin/importmap , добавленную как часть importmap-rails , чтобы привязать, отвязать или обновить пакеты npm в карте импорта. Этот исполняемый файл использует JSPM.org .

Он работает так:

./bin/importmap pin react react-dom Pinning "react" to https://ga.jspm.io/npm:react@17.0.2/index.js Pinning "react-dom" to https://ga.jspm.io/npm:react-dom@17.0.2/index.js Pinning "object-assign" to https://ga.jspm.io/npm:object-assign@4.1.1/index.js Pinning "scheduler" to https://ga.jspm.io/npm:scheduler@0.20.2/index.js ./bin/importmap json  "imports":  "application": "/assets/application-37f365cbecf1fa2810a8303f4b6571676fa1f9c56c248528bc14ddb857531b95.js", "react": "https://ga.jspm.io/npm:react@17.0.2/index.js", "react-dom": "https://ga.jspm.io/npm:react-dom@17.0.2/index.js", "object-assign": "https://ga.jspm.io/npm:object-assign@4.1.1/index.js", "scheduler": "https://ga.jspm.io/npm:scheduler@0.20.2/index.js" > > 

Как видите, два пакета react и react-dom разрешаются в четыре зависимости, при jspm по умолчанию.

Теперь их можно использовать в вашей точке входа application.js , как и любой другой модуль:

import React from "react" import ReactDOM from "react-dom" 

Также можно назначить указанную версию для привязки:

./bin/importmap pin react@17.0.1 Pinning "react" to https://ga.jspm.io/npm:react@17.0.1/index.js Pinning "object-assign" to https://ga.jspm.io/npm:object-assign@4.1.1/index.js 

Или даже убирать привязки:

./bin/importmap unpin react Unpinning "react" Unpinning "object-assign" 

Можно контролировать среду пакета для пакетов с раздельными сборками для «production» (по умолчанию) и «development»:

./bin/importmap pin react --env development Pinning "react" to https://ga.jspm.io/npm:react@17.0.2/dev.index.js Pinning "object-assign" to https://ga.jspm.io/npm:object-assign@4.1.1/index.js 

Также можно подобрать альтернативу, поддерживаемую провайдером CDN, при привязке, например unpkg или jsdelivr (по умолчанию jspm ):

./bin/importmap pin react --from jsdelivr Pinning "react" to https://cdn.jsdelivr.net/npm/react@17.0.2/index.js 

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

Запустите ./bin/importmap , чтобы увидеть все параметры.

Отметьте, что эта команда — просто удобная обертка для разрешения логических имен пакетов к CDN URL. Можно просто найти CDN URL самостоятельно и привязать их. Например, если нужен Skypack для React, можно просто добавить следующее в config/importmap.rb :

pin "react", to: "https://cdn.skypack.dev/react" 
3.4. Предварительная загрузка привязанных модулей

Чтобы избежать эффекта водопада, когда браузеру нужно загружать файлы один за другим, до того, как он доберется до самого глубоко вложенного импорта, importmap-rails поддерживает ссылки modulepreload. Привязанные модули могут быть предварительно загружены, если добавить preload: true к привязке.

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

# config/importmap.rb pin "@github/hotkey", to: "https://ga.jspm.io/npm:@github/hotkey@1.4.4/dist/index.js", preload: true pin "md5", to: "https://cdn.jsdelivr.net/npm/md5@2.3.0/md5.js" # app/views/layouts/application.html.erb %= javascript_importmap_tags %> # включит следующую ссылку перед настройкой importmap: "modulepreload" href="https://ga.jspm.io/npm:@github/hotkey@1.4.4/dist/index.js"> . 

Обратитесь к репозиторию importmap-rails за актуальной документацией.

4. Как использовать Sprockets

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

Sprockets разработан, чтобы автоматически предварительно обрабатывать ваши ассеты, хранимые в сконфигурированных директориях, и после обработки выкладывает их в папку public/assets с метками, сжатием, генерации карт исходников и другими конфигурированными особенностями.

Ассеты все еще можно размещать в иерархии public . Любой ассет в public будет роздан как статичный файл приложением или веб-сервером, когда config.public_file_server.enabled установлена true. Можно определить директивы manifest.js для файлов, которые должны подвергаться некоторой предварительной обработке перед тем, как раздаваться.

В production Rails по умолчанию прекомпилирует эти файлы в public/assets . Прекомпилированные копии затем раздаются как статичные ассеты веб сервером. Файлы в app/assets никогда не раздаются напрямую в production.

4.1. Файлы манифеста и директивы

При компиляции ассетов с помощью Sprockets, ему нужно решить, какие цели верхнего уровня компилировать, обычно application.css и изображения. Цели верхнего уровня определяются в файле Sprockets manifest.js , по умолчанию он выглядит так:

//= link_tree ../images //= link_directory ../stylesheets .css //= link_tree ../../javascript .js //= link_tree ../../../vendor/javascript .js 

Он содержит директивы — инструкции, сообщающие Sprockets, какие файлы требуются, чтобы создать отдельный файл CSS или JavaScript.

Он означает, что надо включить содержимое всех файлов, найденных в директории ./app/assets/images , а также любых поддиректориях, а также любой файл, распознанный как JS, непосредственно в ./app/javascript или ./vendor/javascript .

Он загрузит любой CSS из директории ./app/assets/stylesheets (не включая поддиректории). Допустим, что у нас есть файлы application.css и marketing.css в папке ./app/assets/stylesheets , он позволит загрузить эти таблицы стилей с помощью или из вью.

Возможно, вы заметили, что наши файлы JavaScript не загружаются из директории assets по умолчанию, то потому, что ./app/javascript по умолчанию точка входа для гема importmap-rails , и папка vendor это место, где будут храниться скачанные пакеты JS.

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

Sprockets загружает указанные файлы, при необходимости обрабатывает их, соединяет в единый файл, а затем сжимает (на основании значения config.assets.css_compressor или config.assets.js_compressor ). Сжатие уменьшает размер файла, позволяя браузеру скачивать файлы быстрее.

You can also opt to include controller-specific stylesheets files only in their respective controllers using the following:

 stylesheet_link_tag params[:controller] %> 
4.2. Ассеты конкретного контроллера

При генерации скаффолда или контроллера, Rails также генерирует файл CSS для этого контроллера. Дополнительно при генерации скаффолда, Rails генерирует файл scaffolds.css .

Например, если генерируете ProjectsController , Rails также добавит новый файл app/assets/stylesheets/projects.css . По умолчанию эти файлы будут готовы к немедленному использованию вашим приложением, с помощью директивы link_directory в файле manifest.js .

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

 stylesheet_link_tag params[:controller] %> 

When doing this, ensure you are not using the require_tree directive in your application.css , as that could result in your controller-specific assets being included more than once.

4.3. Asset Organization

Pipeline assets can be placed inside an application in one of three locations: app/assets , lib/assets or vendor/assets .

  • app/assets is for assets that are owned by the application, such as custom images or stylesheets.

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

4.4. Организация ассетов

Ассеты файлопровода могут быть размещены в приложении в одном из этих трех мест расположений: app/assets , lib/assets или vendor/assets .

  • app/assets предназначено для ассетов, принадлежащих приложению, таких как изображения или таблицы стилей, изготовленные специально для приложения.
  • app/javascript для кода JavaScript
  • vendor/[assets|javascript] предназначено для ассетов, принадлежащих сторонним субъектам, таких как фреймворки CSS или библиотеки JavaScript. Имейте в виду, что код третьей стороны со ссылками на другие файлы, также обрабатывающиеся файлопроводом (изображения, таблицы стилей и так далее), должен быть переписан с помощью хелперов, таких как asset_path .

В файле manifest.js можно настроить другие расположения, обратитесь к Файлы манифеста и директивы.

4.4.1. Пути поиска

Когда к файлу обращаются из манифеста или хелпера, Sprockets ищет во всех местах, указанных в manifest.js для него. Путь поиска можно увидеть, просмотрев Rails.application.config.assets.paths в консоли Rails.

4.4.2. Использование индексных файлов как прокси для папок

Sprockets использует файлы с именем index (с соответствующим расширением) для специальных целей.

Например, если имеется библиотека CSS с множеством модулей, хранящаяся в lib/assets/stylesheets/library_namee , файл lib/assets/stylesheets/library_name/index.css служит манифестом для всех файлов в этой библиотеке. Этот файл может включать список всех требуемых файлов в нужном порядке, или просто директиву require_tree .

Это чем-то похоже на способ, которым файл public/library_name/index.html можно достичь запросом к /library_name . Это означает, что нельзя напрямую использовать индексный файл.

Библиотека в целом может быть доступна из файлов .css следующим образом:

/* . *= require library_name */ 

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

4.5. Кодирование ссылок на ассеты

Sprockets не добавляет какие-либо новые методы для доступа к вашим ассетам — используйте знакомый метод stylesheet_link_tag .

 stylesheet_link_tag "application", media: "all" %> 

При использовании гема turbo-rails , который включен по умолчанию в Rails, включите опцию ‘data-turbo-track’, которая вызывает проверку Turbo, что ассет был обновлен, таким образом загружая его на страницу:

 stylesheet_link_tag "application", "data-turbo-track": "reload" %> 

В обычных вью можно получить доступ к изображениям в директории app/assets/images следующим образом:

 image_tag "rails.png" %> 

При условии, что файлопровод включен в вашем приложении (и не отключен в контексте текущей среды), этот файл будет отдан с помощью Sprockets. Если файл существует в public/assets/rails.png , он будет отдан веб-сервером.

Кроме того, запрос файла с хэшем SHA256, такого как public/assets/rails-f90d8a84c707a8dc923fca1ca1895ae8ed0a09237f6992015fef1e11be77c023.png будет обработан тем же образом. Как генерируются эти хэши будет раскрыто позже в этом руководстве в разделе В production.

Изображения также могут быть организованы в поддиректории и могут быть доступны с помощью указания имени директории в теге:

 image_tag "icons/rails.png" %> 

Если вы прекомпилируете ассеты (смотрите раздел В production далее), связывание с несуществующим ассетом вызовет исключение на вызывающей странице. Это также справедливо и для связывания с пустой строкой. Поэтому будьте осторожны при использовании image_tag и других хелперов с данными, предоставленными пользователями.

4.5.1. CSS и ERB

Файлопровод автоматически вычисляет ERB. Это означает, что, если добавить расширение erb к ассету CSS (например, application.css.erb ), будут доступны хелперы, такие как asset_path , в правилах вашего CSS:

.class  background-image: url() > 

Этот фрагмент кода записывает путь к определенному указанному ассету. Этот пример имеет смысл, если имеется изображение в одном из путей загрузки ассетов, такое как app/assets/images/image.png , на которое тут будет ссылка. Если это изображение уже имеется в public/assets как файл с меткой, то будет ссылка на него.

Если хотите использовать data URI — метод встраивания данных изображения непосредственно в файл CSS — используйте хелпер asset_data_uri .

#logo  background: url() > 

Этот фрагмент кода вставит правильно отформатированный URI в код CSS.

Отметьте, что закрывающий тег не может быть в стиле -%> .

4.6. Вызов ошибки, если ассет не найден

Если используется sprockets-rails >= 3.2.0, можно настроить, что произойдет, когда выполнен поиск ассета, и ничего не было найдено. Если выключить «asset fallback», тогда будет вызвана ошибка, когда ассет не может быть найден.

config.assets.unknown_asset_fallback = false 

Если «asset fallback» включен, тогда, когда ассет не может быть найден, вместо этого будет выведен путь, а не вызвана ошибка. Поведение «asset fallback» выключено по умолчанию.

4.7. Включение дайджестов

Можно отключить дайджесты, добавив в config/environments/development.rb :

config.assets.digest = false 

Когда эта опция true, для URL ассета будет генерироваться дайджест.

4.8. Включение карт исходников

Можно включить карты исходников, добавив в config/environments/development.rb :

config.assets.debug = true 

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

Ассеты компилируются и кэшируются при первом запросе после запуска сервера. Sprockets устанавливает HTTP-заголовок контроля кэша must-revalidate для уменьшения нагрузки на последующие запросы — на них браузер получает отклик 304 (Not Modified).

Если какой-либо из файлов в манифесте меняется между запросами, сервер возвращает новый скомпилированный файл.

5. В production

В среде production Sprockets использует схему меток, описанную ранее. По умолчанию Rails полагает, что ассеты прекомпилированы и будут отданы как статичные ассеты вашим веб-сервером.

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

 stylesheet_link_tag "application" %> 

сгенерирует что-то наподобие этого:

 href="/assets/application-4dd5b109ee3439da54f5bdfd78a80473.css" rel="stylesheet" /> 

Режим меток контролируется с помощью инициализационной опции config.assets.digest (которая по умолчанию true ).

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

5.1. Прекомпиляция ассетов

В Rails имеется встроенная команда для компиляции на диск манифестов ассетов и других файлов в файлопроводе.

Скомпилированные ассеты записываются в место расположения, указанное в config.assets.prefix . По умолчанию это директория /assets .

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

$ RAILS_ENV=production rails assets:precompile 

Это свяжет папку, указанную в config.assets.prefix с shared/assets . Если вы уже используете эту общую папку, вам следует написать собственную команду для деплоя.

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

Всегда определяйте ожидаемое имя скомпилированного файла, оканчивающееся на .js или .css .

Команда также генерирует .sprockets-manifest-randomhex.json (где randomhex — это 16-байтовая случайная шестнадцатеричная строка), который содержит список всех ваших ассетов и соответствующие им метки. Это используется методами хелпера Rails, чтобы избежать направления запроса в Sprockets. Обычный файл манифеста выглядит так:

"files":"application-.js":"logical_path":"application.js","mtime":"2016-12-23T20:12:03-05:00","size":412383, "digest":"","integrity":"sha256-">>, "assets":"application.js":"application-.js">> 

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

Место расположения манифеста по умолчанию — корень папки, определенной в config.assets.prefix (по умолчанию ‘/assets’).

Если в production отсутствуют прекомпилированные файлы, вы получите исключение Sprockets::Helpers::RailsHelper::AssetPaths::AssetNotPrecompiledError , указывающее имя отсутствующего файла(-ов).

5.1.1. Вечный заголовок Expires

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

# Директивы Expires* требуют, чтобы модуль Apache `mod_expires` был включен. Location /assets/> # Не рекомендуется использование ETag, когда присутствует Last-Modified Header unset ETag FileETag None # RFC предписывает кэшировать только на 1 год ExpiresActive On ExpiresDefault "access plus 1 year" Location> 
location ~ ^/assets/  expires 1y; add_header Cache-Control public; add_header ETag ""; > 
5.2. Локальная прекомпиляция

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

В таких случаях можно прекомпилировать ассеты локально — то есть добавить окончательный набор скомпилированных, готовых к production ассетов в репозиторий исходного кода перед отправлением на production. Таким образом, их не нужно прекомпилировать отдельно на сервере production при каждом деплое.

Как указано выше, это можно сделать с помощью

$ RAILS_ENV=production rails assets:precompile 

Есть следующие оговорки:

    Если доступны прекомпилированные ассеты, они будут отданы, даже если они больше не соответствуют оригинальным (не компилированным) ассетам, даже на сервере development. Чтобы убедиться, что сервер development всегда компилирует ассеты на лету (и, таким образом, всегда отражает последнее состояние кода), среда development должна быть настроена содержать прекомпилированные ассеты в другом месте, чем содержит production. В противном случае, любые ассеты, прекомпилированные для использования в production, будут ломать запросы к ним в development (например, последующие сделанные изменения в ассетах не будут отражены в браузере). Это можно сделать, добавив следующую строчку в config/environments/development.rb :

config.assets.prefix = "/dev-assets" 

Также можно установить ENV[«SECRET_KEY_BASE_DUMMY»] , чтобы запустить использование случайно сгенерированного secret_key_base , хранящегося во временном файле. Это полезно при прекомпиляции ассетов для production как части шага сборки, которому тогда не нужен доступ к секретам production.

$ SECRET_KEY_BASE_DUMMY=1 bundle exec rails assets:precompile 
5.3. Компиляция в реальном времени

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

Чтобы включить эту опцию, установите:

config.assets.compile = true 

При первом запросе ассеты компилируются и кэшируются так, как описывается в разделе про Хранилище кэша ассетов, и имена манифеста, использованного в хелперах, изменяется путем включения хэша SHA256.

Sprockets также устанавливает HTTP-заголовок Cache-Control как max-age=31536000 . Это сигнализирует всем кэшам между вашим сервером и браузером клиента, что это содержимое (отданный файл) может быть закэшировано на 1 год. В результате уменьшается количество запросов для этого ассета на ваш сервер; есть хороший шанс, что ассет будет в локальном кэше браузера или в каком-либо промежуточном кэше.

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

5.4. CDN

CDN расшифровывается как Content Delivery Network, она в основном предназначена для кэширования ассетов по всему миру, поэтому когда браузер запрашивает ассет, кэшированная копия будет географически ближайшая к этому браузеру. Если отдавать ассеты непосредственно от сервера Rails в production, лучшей практикой будет использовать CDN перед приложением.

Обычным образцом использования CDN является установка вашего приложения в production как «origin» сервер. Это означает, что когда браузер запрашивает ассет из CDN, и кэш отсутствует, он возьмет файл с вашего сервера на лету и кэширует его. Например, если вы запустили приложение Rails на example.com , и у вас настроен CDN на mycdnsubdomain.fictional-cdn.com , то, когда делается запрос к mycdnsubdomain.fictional-cdn.com/assets/smile.png , CDN единожды запросит ваш сервер на example.com/assets/smile.png и кэширует запрос. Следующий запрос к CDN, пришедший по тому же самому URL, получит кэшированную копию. Когда CDN может отдать ассет напрямую, запрос никогда не затронет сервер Rails. Так как ассеты из CDN географически ближе к браузеру, запрос быстрее, и, так как серверу не нужно тратить время на раздачу ассетов, он может сфокусироваться на как можно быстром обслуживании кода приложения.

5.4.1. Настройка CDN на раздачу статических ассетов

Для настройки CDN вам нужно, чтобы ваше приложение было запущено в production в интернете на публично доступном URL, например example.com . Далее необходимо зарегистрироваться на сервисе CDN облачного провайдера. После этого необходимо настроить «origin» для CDN, указав ваш сайт example.com . Обратитесь к документации провайдера по настройке origin-сервера.

Подготовленный CDN даст определенный поддомен для вашего приложения, такой как mycdnsubdomain.fictional-cdn.com (отметьте, что fictional-cdn.com это не существующий провайдер CDN в настоящее время). Теперь, когда есть настроенный сервер CDN, необходимо сообщить браузерам использовать ваш CDN для того, чтобы брать ассеты оттуда, а не от сервера Rails. Это можно осуществить, настроив Rails, установив ваш CDN в качестве хоста ассетов, вместо использования относительного пути. Для настройки хоста ассетов в Rails, необходимо установить config.asset_host в config/environments/production.rb :

config.asset_host = 'mycdnsubdomain.fictional-cdn.com' 

Необходимо предоставить только «host», это поддомен и корневой домен, не нужно указывать протокол или «scheme», такие как http:// или https:// . Когда запрашивается страница, протокол в сгенерированной ссылке на ассет будет соответствовать тому, какой доступ к странице.

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

config.asset_host = ENV['CDN_HOST'] 

Чтобы это работало, вам необходимо установить на сервере CDN_HOST значение mycdnsubdomain.fictional-cdn.com .

После того, как вы настроили свой сервер и ваш CDN, пути ассета из хелперов такие как:

 asset_path('smile.png') %> 

Будут отрендерены полные пути к CDN, наподобие http://mycdnsubdomain.fictional-cdn.com/assets/smile.png (дайджест опущен для читаемости).

Если на CDN имеется копия smile.png , она будет отдана браузеру, и ваш сервер даже не узнает, что она была запрошена. Если на CDN нет копии, он попытается найти ее на «origin» example.com/assets/smile.png , а затем сохранить ее для дальнейшего использования.

Если хотите отдавать только некоторые ассеты из CDN, можно использовать опцию :host в хелпере ассета, переопределяющую значение, установленное в config.action_controller.asset_host .

 asset_path 'image.png', host: 'mycdnsubdomain.fictional-cdn.com' %> 
5.4.2. Настройка поведения кэширования CDN

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

5.4.2.1. Кэширование запросов CDN

Хотя CDN описывается как кэширующий файлы ассетов, фактически он кэширует целые запросы. Они включают тело ассета, а также его заголовки. Наиболее важным является Cache-Control , который сообщает CDN (и браузерам), как кэшировать содержимое. Это означает, что если кто-то запрашивает несуществующий ассет, наподобие /assets/i-dont-exist.png , и ваше приложение Rails возвращает 404, тогда ваш CDN скорее всего закэширует страницу 404, если присутствует валидный заголовок Cache-Control .

5.4.2.2. Отладка заголовков CDN

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

$ curl -I http://www.example/assets/application- d0e099e021c95eb0de3615fd1d8c4d83.css HTTP/1.1 200 OK Server: Cowboy Date: Sun, 24 Aug 2014 20:27:50 GMT Connection: keep-alive Last-Modified: Thu, 08 May 2014 01:24:14 GMT Content-Type: text/css Cache-Control: public, max-age=2592000 Content-Length: 126560 Via: 1.1 vegur 

Против копии на CDN:

$ curl -I http://mycdnsubdomain.fictional-cdn.com/application- d0e099e021c95eb0de3615fd1d8c4d83.css HTTP/1.1 200 OK Server: Cowboy Last- Modified: Thu, 08 May 2014 01:24:14 GMT Content-Type: text/css Cache-Control: public, max-age=2592000 Via: 1.1 vegur Content-Length: 126560 Accept-Ranges: bytes Date: Sun, 24 Aug 2014 20:28:45 GMT Via: 1.1 varnish Age: 885814 Connection: keep-alive X-Served-By: cache-dfw1828-DFW X-Cache: HIT X-Cache-Hits: 68 X-Timer: S1408912125.211638212,VS0,VE0 

Проверьте документацию вашего CDN, чтобы найти подробности о том, что такое X-Cache или любые другие добавленные ими заголовки.

5.4.2.3. CDN и заголовок Cache-Control

Заголовок Cache-Control описывает, как может быть закэширован запрос. Когда не используется CDN, браузер использует эту информацию для кэширования содержимого. Это очень полезно для ассетов, которые не модифицированы, так как браузеру не нужно повторно скачивать CSS или JavaScript сайта при каждом запросе. Как правило, мы хотим, чтобы наш сервер Rails сообщил нашему CDN (и браузеру), что ассет «public». Это означает, что любой кэш может сохранять запрос. Также мы в основном хотим установить max-age , который означает, как долго кэш будет хранить объект до недействительности кэша. Значение max-age устанавливается в секундах с максимально возможным значением 31536000 , равным одному году. Это можно сделать в вашем приложении Rails, установив

config.public_file_server.headers =  'Cache-Control' => 'public, max-age=31536000' > 

Теперь, когда ваше приложение отдает ассет в production, CDN сохранит ассет на один год. Так как большинство CDN также кэшируют заголовки запроса, этот Cache-Control будет передан всем браузерам, обращающимся к этому ассету. Браузер тогда будет знать, что он может хранить этот ассет очень долго без необходимости повторного запроса.

5.4.2.4. CDN и недействительность кэша, основанного на URL

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

http://mycdnsubdomain.fictional-cdn.com/assets/smile-123.png 

Будет полностью по-другому закэширован, чем

http://mycdnsubdomain.fictional-cdn.com/assets/smile.png 

Если хотите установить длительный max-age в вашем Cache-Control (и делаете так), то убедитесь, что, когда вы изменяете ассеты, ваш кэш прекращается. Например, при изменении рожицы смайлика в изображении с желтого на синий, вы хотите, чтобы все посетители вашего сайта получили новую синюю рожицу. При использовании CDN с настройкой файлопровода Rails config.assets.digest , установленной true по умолчанию, каждый ассет будет иметь другое имя, если он изменится. Таким образом, вам даже не нужно вручную прекращать любые элементы в вашем кэше. Используя иную технику для уникального имени ассета, ваши пользователи также получат самый свежий ассет.

6. Настройка файлопровода

6.1. Сжатие CSS

Одним из вариантов для сжатия CSS является YUI. YUI CSS compressor предоставляет минификацию.

Следующая строчка включает сжатие YUI и требует гем yui-compressor .

config.assets.css_compressor = :yui 
6.2. Сжатие JavaScript

Возможные варианты для сжатия JavaScript это :terser , :closure и :yui . Они требуют использование гемов terser , closure-compiler или yui-compressor соответственно.

Возьмем, к примеру, гем terser . Этот гем оборачивает Terser (написанный для Node.js) в Ruby. Он сжимает ваш код, убирая пробелы и комментарии, сокращая имена локальных переменных и выполняя иные микро-оптимизации, наподобие замены ваших выражений if и else на тернарные операторы там, где возможно.

Следующая строчка вызывает terser для сжатия JavaScript.

config.assets.js_compressor = :terser 

Необходим runtime, поддерживаемый ExecJS, чтобы использовать terser . Если используете macOS или Windows, у вас уже имеется JavaScript runtime, установленный в операционной системе.

Компрессия JavaScript также будет работать для ваших файлов JavaScript, когда вы загружаете свои ассеты через гемы importmap-rails или jsbundling-rails .

6.3. Сжатие ассетов

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

config.assets.gzip = false # отключает генерацию сжатых ассетов 

Обратитесь к документации своего веб-сервера за инструкцией, как раздавать сжатые ассеты.

6.4. Использование собственного компрессора

Настройки конфигурации компрессора для CSS и JavaScript также могут принимать любой объект. Этот объект должен иметь метод compress , принимающий строку как единственный аргумент, и он должен возвращать строку.

class Transformer def compress(string) do_something_returning_a_string(string) end end 

Чтобы включить это, передайте новый объект в конфигурационную опцию в application.rb :

config.assets.css_compressor = Transformer.new 
6.5. Изменение пути assets

Публичный путь, используемый Sprockets по умолчанию, это /assets .

Он может быть заменен на что-то другое:

config.assets.prefix = "/some_other_path" 

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

6.6. Заголовки X-Sendfile

Заголовок X-Sendfile — это указание веб-серверу игнорировать отклик от приложения, и вместо этого отдать определенный файл с диска. Эта опция отключена по умолчанию, но может быть включена, если ее поддерживает сервер. Когда опция включена, обязанность по отдаче файла передается веб-серверу, который справляется с ней быстрее. Обратитесь к send_file, чтобы узнать, как использовать эту особенность.

Apache и NGINX поддерживают эту опцию. Она включается в config/environments/production.rb .

# config.action_dispatch.x_sendfile_header = "X-Sendfile" # для Apache # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # для NGINX 

Если вы производите апгрейд существующего приложения и намереваетесь использовать эту опцию, убедитесь, что скопировали ее только в production.rb и в любую другую среду, которую вы определили, как имеющую поведение production (не в application.rb ).

За дальнейшими подробностями обращайтесь к документации своих веб-серверов:

7. Хранилище кэша ассетов

По умолчанию Sprockets кэширует ассеты в tmp/cache/assets в development и production. Это может быть изменено следующим образом:

config.assets.configure do |env| env.cache = ActiveSupport::Cache.lookup_store(:memory_store,  size: 32.megabytes >) end 

Чтобы отключить хранилище кэша ассетов:

config.assets.configure do |env| env.cache = ActiveSupport::Cache.lookup_store(:null_store) end 

8. Добавление ассетов в ваши гемы

Ассеты также могут идти от внешних источников в виде гемов.

Хорошим примером этого является гем jquery-rails . Этот гем содержит класс engine, унаследованный от Rails::Engine . Сделав так, Rails становится проинформированным, что директории для этого гема могут содержать ассеты, и директории app/assets , lib/assets и vendor/assets этого engine добавляются в путь поиска Sprockets.

9. Создание препроцессора в вашей библиотеке или геме

Sprockets использует Процессоры, Трансформеры, Компрессоры и Экспортеры для расширения функциональности Sprockets. Обратитесь к Расширение Sprockets, чтобы узнать больше об этом. Здесь мы зарегистрировали препроцессор, чтобы добавить комментарий в конец text/css ( .css ) файлов.

module AddComment def self.call(input)  data: input[:data] + "/* Hello From my sprockets extension */" > end end 

Теперь, когда у вас есть модуль, который модифицирует входные данные, самое время зарегистрировать его как препроцессор для вашего типа MIME.

Sprockets.register_preprocessor 'text/css', AddComment 

10. Альтернативные библиотеки

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

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

10.1. jsbundling-rails

jsbundling-rails это зависимая от Node.js альтернатива способу importmap-rails сборки JavaScript с помощью esbuild, rollup.js или Webpack.

Гем предоставляет процесс yarn build —watch для автоматической генерации вывода в development. Для production, он автоматически прицепляет задачу javascript:build к задаче assets:precompile , чтобы обеспечить, что все ваши зависимости от пакетов были установлены, и JavaScript был создан для всех точек входа.

Когда использовать вместо importmap-rails ? Если ваш код JavaScript зависит от транспиляции, то есть если вы используете Babel, TypeScript или формат React JSX , то jsbundling-rails правильный способ.

10.2. Webpacker/Shakapacker

Webpacker был препроцессором JavaScript по умолчанию и сборщиком для Rails 5 и 6. Теперь он не разрабатывается. Существует преемник, называющийся shakapacker , но он не поддерживается командой или проектом Rails.

В отличие от других библиотек в этом списке, webpacker / shakapacker полностью не зависят от Sprockets и могут обрабатывать файлы как JavaScript, так и CSS.

Прочитайте документ по сравнению с Webpacker, чтобы понять разницу между jsbundling-rails и webpacker / shakapacker .

10.3. cssbundling-rails

cssbundling-rails позволяет сборку и обработку ваших CSS с помощью Tailwind CSS, Bootstrap, Bulma, PostCSS или Dart Sass, а затем доставляет CSS посредством файлопровода.

Он работает схожим образом с jsbundling-rails , поэтому добавьте зависимость от Node.js в ваше приложение с помощью процесса yarn build:css —watch , чтобы заново сгенерировать ваши таблицы стилей в development и прицепляется к задаче assets:precompile в production.

Какое отличие от Sprockets? Sprockets сам по себе не способен транспилировать Sass в CSS, требуется Node.js, чтобы сгенерировать файлы .css из файлов .sass . Как только сгенерированы файлы .css , тогда Sprockets сможет доставить их вашим клиентам.

cssbundling-rails полагается на Node для обработки CSS. Гемы dartsass-rails и tailwindcss-rails это самостоятельные версии Tailwind CSS и Dart Sass, что означает отсутствие зависимости от Node. Если вы используете importmap-rails для обработки Javascript и dartsass-rails или tailwindcss-rails для CSS, можно полностью избежать зависимости от Node, что приведет к менее сложному решению.

10.4. dartsass-rails

Если хотите использовать Sass в приложении, dartsass-rails пришел на замену для устаревшего гема sassc-rails . dartsass-rails использует реализацию Dart Sass вместо устаревшего в 2020 году LibSass , используемого в sassc-rails .

В отличие от sassc-rails , новый гем напрямую не интегрируется со Sprockets . Обратитесь к домашней странице гема за инструкциями по установке/миграции.

Популярный гем sassc-rails не поддерживается с 2019 года.

10.5. tailwindcss-rails

tailwindcss-rails это гем-обертка для самостоятельной выполняемой версии фреймворка Tailwind CSS v3. Используется для новых приложений, когда предоставлена —css tailwind для команды rails new . Предоставляет процесс watch , чтобы автоматически сгенерировать вывод Tailwind в development. Для production он прицепляется к задаче assets:precompile .

Лицензия CC BY-SA 4.0 «Rails», «Ruby on Rails» и логотип Rails — торговые марки DHH

Обзор техник кэширования в React

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

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

Данные

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

export default function handler(req, res) setTimeout( 
() =>
res.status(200).json( randomNumber: Math.round(Math.random() * 10000),
people: [
< name: "John Doe" >,
< name: "Olive Yew" >,
< name: "Allie Grater" >,
],
>),
750
);
>

Этот код выполняется при осуществлении запроса к пути проекта /api/people . Как видно, мы возвращаем объект с двумя свойствами:

  • randomNumber : произвольное число в диапазоне 0–10000.
  • people : статический массив с тремя вымышленными именами.

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

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

Компонент People

Отображая данные из API, мы передаем их в компонент с именем PeopleRenderer . Выглядит это следующим образом:

Имея в виду эту вводную информацию, рассмотрим первую технику.

useEffect

Внутри компонентов для получения данных можно задействовать хук useEffect , после чего сохранять эти данные локально (внутри компонента) с помощью useState :

import < useEffect, useState >from "react";
import PeopleRenderer from "../PeopleRenderer";

export default function PeopleUseEffect() const [json, setJson] = useState(null);

useEffect(() => fetch("/api/people")
.then((response) => response.json())
.then((data) => setJson(data));
>, []);

return />;
>

При передаче пустого массива в качестве второго параметра (строка 11) хук useEffect будет выполнен после встраивания компонента в DOM. При новом отображении компонента повторно он выполняться уже не будет. Назовем этот хук “одноразовым”.

В связи с применением useEffect таким способом следует упомянуть о том, что при наличии нескольких экземпляров компонента в DOM все они будут получать данные по отдельности (при встраивании).

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

Мемоизация

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

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

const MyMemoizedFunction = (age) => if(cache.hasKey(age)) return cache.get(age); 
>
const value = `You are $ years old!`;
cache.set(age, value);
return value;
>

Написание такого шаблонного кода вскоре может стать трудоемким процессом, поэтому такие известные библиотеки, как Lodash и Underscore, предоставляют вспомогательные функции, упрощая создание мемоизованных:

import memoize from "lodash.memoize";

const MyFunction = (age) => return `You are $ years old!`;
>
const MyMemoizedFunction = memoize(MyFunction);

Мемоизация для получения данных

Данная техника подходит для получения данных. Мы создаем функцию getData , возвращающую обещание Promise , разрешение которого происходит по окончании fetch-запроса. Мы сохраняем этот Promise :

import memoize from "lodash.memoize";

const getData = () =>
new Promise((resolve) => fetch("http://localhost:3000/api/people")
.then((response) => response.json())
.then((data) => resolve(data));
>);

export default memoize(getData);

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

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

import < useEffect, useState >from "react";
import getData from "./getData";
import PeopleRenderer from "../PeopleRenderer";

export default function PeopleMemoize() const [json, setJson] = useState(null);

useEffect(() => getData().then((data) => setJson(data));
>, []);

return />;
>

Поскольку результат getData сохраняется, то при встраивании все компоненты получат одинаковые данные:

Стоит также отметить, что при открытии страницы memoize.tsx (до добавления первого экземпляра компонента) данные уже предварительно получаются. Дело в том, что мы определили функцию getData в отдельном расположенном в верхней части страницы файле, при загрузке которого создается Promise .

Можно аннулировать (очистить) кэш мемоизованной функции, присвоив ее свойству cache нового Cache .

getData.cache = new memoize.Cache();

В качестве альтернативного варианта можно очистить существующий кэш (это экземпляр Map ).

getData.cache.clear();

Однако данная функциональность характерна лишь для Lodash. Другие же библиотеки требуют иных решений. Ниже представлен наглядный пример очистки кэша в действии.

React Context

React Context — еще один известный и подробно изученный инструмент (в отношении которого однако часто происходит недопонимание). В связи с этим в очередной раз напоминаю, что он не заменяет такие инструменты, как Redux, поскольку не является средством управления состоянием.

Итак, что такое Context? Это механизм для внедрения данных в дерево компонентов. Если у вас есть некие данные, то их можно сохранить, к примеру, с помощью хука useState , внутри компонента, находящегося на более высоком уровне иерархии. Затем с помощью Provider Context внедрить эти данные в дерево, что позволит считывать (использовать) их в любом нижестоящем компоненте.

Для большей ясности приведем пример. Сначала создаем новый контекст.

import < createContext >from "react";

export const PeopleContext = createContext(null);

После этого обертываем компонент, отображающий компоненты People , в Provider Context.

export default function Context() const [json, setJson] = useState(null);

useEffect(() => fetch("/api/people")
.then((response) => response.json())
.then((data) => setJson(data));
>, []);

return (
>>
.

);
>

В 12 строке у нас есть возможность отобразить любой элемент. В определенный момент, опускаясь вниз по дереву, отобразим компонент(ы) People .

import < useContext >from "react";
import PeopleRenderer from "../PeopleRenderer";
import < PeopleContext >from "./context";

export default function PeopleWithContext() const < json >= useContext(PeopleContext);

return />;
>

Можно применить значение из Provider с помощью хука useContext , получая следующий результат.

Обратите внимание на одно важное здесь отличие! На последнем этапе анимации мы нажимаем кнопку “Set new seed”. При этом повторно получаются данные, хранящиеся в Provider Context. По завершении этого (спустя 750 мс) полученные данные становятся новым значением Provider , и компоненты People отображаются еще раз. Как видите, все они пользуются одними и теми же общими данными.

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

useMemo

В последнем, но не маловажном разделе, проведем беглый обзор useMemo . От предыдущих техник этот хук отличается тем, что он является лишь формой кэширования на локальном уровне: внутри одного экземпляра компонента. useMemo не предназначен для совместного использования данных несколькими компонентами — по крайней мере, не без обходных решений в виде пробрасывания (prop-drilling) пропсов или внедрения зависимостей (например, React Context).

useMemo — это инструмент оптимизации. Он позволяет избежать пересчета значения при каждом повторном отображении компонента. Нет лучшего объяснения, чем сама документация, но рассмотрим пример.

export default function Memo() const getRnd = () => Math.round(Math.random() * 10000);

const [age, setAge] = useState(24);
const [randomNumber, setRandomNumber] = useState(getRnd());

const pow = useMemo(() => Math.pow(age, 2) + getRnd(), [age]);

return (
.
);
>
  • getRnd (строка 2): функция, возвращающая случайное число в диапазоне 0–10000.
  • age (строка 4): с помощью useState сохраняет число, обозначающее возраст.
  • randomNumber (строка 5): сохраняет случайное число посредством useState .

И наконец, в строке 7 применяется useMemo . Мы запоминаем результат вызова функции и сохраняем его в переменной pow . Функция возвращает значение суммы age , возведенной во вторую степень, и случайного числа. Поскольку она зависит от переменной age , мы передаем эту переменную в аргумент зависимости вызова useMemo .

Функция выполняется только в случае изменения значения age . Если компонент отображается повторно, а значение age не изменилось, useMemo просто вернет мемоизованный результат.

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

Две последние анимации показывают, что происходит. Сначала мы обновляем randomNumber , не затрагивая значение age , вследствие чего наблюдаем useMemo в действии (значение pow не меняется при повторном отображении компонента). При каждом нажатии на кнопку компонент заново отображается.

Однако изменение значения age повлечет за собой изменение pow , поскольку вызов useMemo зависит от значения age .

Заключение

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

С полным вариантом кода, представленного в статье, можно ознакомиться в репозитории на GitLab.

Спасибо за внимание!

  • 3 способа улучшить управление состоянием в React
  • Как сделать приложение с дополненной реальностью, используя React Native
  • Управляйте приложением React с помощью голоса

Как работают нативные JavaScript imports

Владимир Шайтан

Как работают нативные JavaScript imports

Lead Front-end Developer в One Source, Преподаватель Компьютерной школы Hillel.

  1. 1. Навіщо потрібні import-и ?
  2. 2. Как работают import-ы
  3. 3. Как V8 JavaScript Engine делает импорты?
  4. 4. Как использовать import?
  5. 5. О динамическом импорте
  6. 6. Интересное
  7. 7. Вывод

Стандарт EcmaScript 2015 (ES6) ввел новый инструмент для работы с модулями JavaScript, известный как import. Этот инструмент позволяет легко импортировать функции, классы, переменные и объекты из других файлов, создавая модульную структуру вашего кода.

В этой статье мы рассмотрим, как работают нативные JavaScript import-ы. Важно понимать, что в этом материале мы не затрагиваем тему того, как работают импорты в инструментах сборки, например, webpack.

Зачем нужны import-ы?

Модульный код помогает разделить программу на логические части (модули), что облегчает организацию, поддержку и расширение кода.

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

Как работают import-ы

Импорты в JavaScript определяются спецификацией EcmaScript (ES6 и более поздних версий). Под капотом механизм импорта включает в себя несколько ключевых аспектов:

Размещение файлов

Когда вы производите импорт в JavaScript, вы указываете путь к файлу, который нужно импортировать. Этот путь может быть абсолютным или относительным, и браузер (или среда выполнения, если это Node.js) выполняет запрос на сервер для загрузки этого файла.

HTTP-запрос

Браузер (или среда выполнения) делает HTTP-запрос на сервер для загрузки файла, указанного в импорте. Это может быть GET-запросом или другими HTTP-методами.

Загрузка файла

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

Выполнение кода

После загрузки файла содержимое файла обрабатывается и выполняется в среде выполнения (браузер или Node.js). Если файл содержит импорты, импортируемые модули также загружаются и выполняются.

Область видимости

Импортируемые переменные или функции становятся доступными в области видимости импортируемого файла.

// module.js export function greet(name) < console.log(`Hello, $!`); > // main.js import < greet >from './module.js'; greet('Alice');

Во время выполнения main.js браузер (или Node.js) выполнит HTTP-запрос в файл module.js, загрузит его содержимое, выполнит функцию greet и выведет «Hello, Alice!».

Как V8 JavaScript Engine делает импорты?

V8 — это виртуальная машина JavaScript, которая используется в браузерах и Node.js для выполнения JavaScript-кода. V8 взаимодействует с импортами в JavaScript через стандартные механизмы ECMAScript (ES6 и более поздних версий).

Рассмотрим, как V8 обрабатывает импорты:

Анализ и парсинг:

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

Распознавание путей:

Во время парсинга V8 распознает пути к импортируемым файлам. Он определяет, являются ли эти пути абсолютными (начинаются с `/`) или относительными (начинаются с `./` или `../`). Внутренняя логика определяет, какой файл нужно скачать.

Загрузка файлов:

V8 инициирует загрузку файлов, указанных в импортах.

В веб-браузерах это может включать выполнение HTTP-запроса на сервер с помощью API, такого как `fetch`, для получения содержимого файла. В среде Node.js это может включать в себя обращение к файловой системе для чтения файла с диска.

Кэширование импортов:

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

Выполнение кода:

После загрузки V8 обрабатывает содержимое импортированных файлов и выполняет код JavaScript.

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

Построение зависимостей:

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

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

События и обработка ошибок:

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

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

Область видимости:

После загрузки и выполнения модуля импортируемые объекты (функции, переменные, классы) становятся доступными в области видимости модуля, который делает импорт.

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

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

Как использовать import?

Импорты в JavaScript (JS) и TypeScript (TS) — это способ включить функциональность из других модулей (файлов) в ваш код. Это помогает организовать проект и поддерживать чистоту кода, разделяя его на меньшие, самостоятельные части.

У JavaScript и TypeScript существует несколько способов импорта. Рассмотрим основные с простейшими примерами на языке JavaScript.

Default Imports (импорт по умолчанию):

В файле module.js мы можем иметь функцию по умолчанию, которую мы хотим импортировать.

// module.js export default function greet(name) < console.log(`Hello, $!`); >

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

// main.js import greet from './module.js'; greet('Alice');

Named Imports (импорт по имени):

// module.js export function greet(name) < console.log(`Hello, $!`); > export function farewell(name) < console.log(`Goodbye, $!`); >

В другом файле мы импортируем их так:

// main.js import < greet, farewell >from './module.js'; greet('Charlie'); farewell('David');

Aliasing (импорт с псевдонимами):

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

// module.js export function greet(name) < console.log(`Hello, $!`); > // main.js import < greet as sayHello >from './module.js'; sayHello('Grace');

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

О динамическом импорте

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

Синтаксис

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

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

const dynamicImport = import('./myModule.js');

Использование Promise:

Объект, возвращаемый из import(), является обещанием (Promise). Вы можете использовать метод .then() или ключевое слово await для решения этого обещания и выполнения кода, когда модуль загружен.

Пример использования .then():

dynamicImport .then((module) => < // Використання завантаженого модуля >) .catch((error) => < // Обробка помилки завантаження >);

Пример использования await:

try < const module = await dynamicImport; // Використання завантаженого модуля >catch (error) < // Обробка помилки завантаження >

Ленивая загрузка

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

В зависимости от переменных

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

const moduleName = 'myModule'; const dynamicImport = import(`./$.js`);

Интересное

Импорт в форме export* from …

Вы можете импортировать все экспорты из другого модуля с помощью export*.

Это позволяет импортировать все функции и переменные из другого модуля в текущий модуль.

export * from './myModule.js';

Внешний импорт файлов

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

Это позволяет включать содержание файлов непосредственно в свой код.

import myImage from './myImage.png';

Модульные импорты в HTML

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

  

Динамические ключи для импорта

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

const moduleName = 'myModule'; import(`./$.js`) .then((module) => < // Використання завантаженого модуля >);

Вывод

Использование импортов в JavaScript имеет множество преимуществ:

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

  • Разделение функциональности:

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

  • Сохранность ресурсов:

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

  • Зависимость и поддержка:

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

  • Ленивая загрузка:

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

  • Удобство и читабельность:

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

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

Рекомендуем курс по теме

JavaScript Basiс basic

Владимир Шайтан

Lead Front-end Developer в One Source, Преподаватель Компьютерной школы Hillel.

Методы оптимизации JavaScript для более быстрой загрузки веб-сайта. Подробное руководство

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

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

Минимизация размеров файлов

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

Пример кода JavaScript: до и после минификации

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

// Функция вычисления суммы двух чисел function addNumbers(num1, num2) < return num1 + num2; >// Вызов функции для вычисления суммы 3 и 5 const sum = addNumbers(3, 5); // Вывод результата в консоль console.log("The sum is:", sum);
function addNumbers(n,e)const sum=addNumbers(3,5);console.log("The sum is:",sum);

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

Сжатие файлов

Сжатие — это еще один метод, используемый для уменьшения размера файлов, что приводит к сокращению времени загрузки веб-сайта. Он работает, применяя алгоритмы для сжатия данных в файлах, делая их меньше без потери их функциональности. Когда браузер запрашивает сжатый файл, он распаковывается на лету, что позволяет правильно отображать и выполнять содержимое. Существует два широко используемых алгоритма сжатия файлов JavaScript: Gzip и Brotli. Gzip долгое время был стандартом де-факто, но Brotli, новый алгоритм сжатия, разработанный Google, становится все более популярным благодаря превосходной степени сжатия и скорости.

Методы сжатия Gzip и Brotli

Gzip: Gzip — это широко распространенный алгоритм сжатия, который может значительно уменьшить размер файлов JavaScript. Gzip использует алгоритм Deflate, который сочетает в себе кодирование LZ77 и Huffman для эффективного сжатия данных. Brotli: Brotli — это более новый алгоритм сжатия, разработанный Google, обеспечивающий лучшую степень сжатия, чем Gzip. Brotli использует комбинацию LZ77, кодирования Хаффмана и новой техники моделирования контекста для достижения более высоких степеней сжатия. В большинстве случаев Brotli превосходит Gzip как по степени сжатия, так и по скорости, что делает его привлекательным вариантом для современных веб-приложений.

Конфигурация на стороне сервера для сжатия

  • Apache: Включите модуль mod_deflate для сжатия Gzip или mod_brotli для сжатия Brotli и настройте соответствующие параметры в файле .htaccess или конфигурации виртуального хоста.
  • Nginx: Используйте директивы gzip или brotli в конфигурационном файле Nginx, чтобы включить сжатие и указать настройки.
  • Node.js: Для серверов на базе Node.js вы можете использовать промежуточное программное обеспечение (middleware), такое как compression для Gzip или shrink-ray-current для Brotli в сочетании с Express или аналогичным веб-фреймворком.

Бандлинг для сокращения сетевых запросов

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

Что такое бандлинг?

Бандлинг — это процесс объединения нескольких файлов JavaScript в один файл. Это уменьшает количество HTTP-запросов, которые необходимо сделать браузеру, тем самым ускоряя процесс загрузки. Объединение может значительно повысить производительность веб-сайта, особенно для веб-сайтов с большим количеством файлов JavaScript меньшего размера.

Инструменты для бандлинга

  • Webpack: Webpack — это мощный и гибкий сборщик модулей, который не только объединяет файлы JavaScript, но и обрабатывает другие ресурсы, такие как css и изображения. Он имеет надежную экосистему плагинов, позволяющую расширять его функциональность по мере необходимости.
  • Rollup: Rollup — еще один популярный сборщик модулей JavaScript, ориентированный на простоту и производительность. Он особенно хорошо подходит для объединения библиотек и может выводить несколько форматов, включая модули CommonJS, AMD и ES.

Пример кода JavaScript: объединение нескольких файлов

Чтобы продемонстрировать процесс объединения, предположим, что у вас есть три отдельных файла JavaScript:

// main.js import < greet >from './greeting.js'; import < calculate >from './math.js'; console.log(greet('John')); console.log(calculate(5, 3));
// greeting.js export function greet(name) < return `Hello, $!`; >
// math.js export function calculate(x, y)

Используя инструмент бандлинга, такой как Webpack или Rollup, вы можете объединить эти файлы в один связанный файл. Выходные данные могут выглядеть примерно так:

(function () < 'use strict'; function greet(name) < return `Hello, $!`; > function calculate(x, y) < return x * y; >console.log(greet('John')); console.log(calculate(5, 3)); >)();

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

Использование спрайтов для изображений и иконок

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

Что такое спрайт изображения

Спрайт изображения — это большое изображение, содержащее несколько изображений меньшего размера, расположенных в виде сетки. В коде CSS или JavaScript вы можете ссылаться на отдельные изображения в спрайте, указывая их положение и размеры. Этот метод позволяет загружать множество изображений с помощью всего одного HTTP-запроса, уменьшая задержку и сокращая время загрузки.

Создание спрайтов изображений

  • Инструменты генератора спрайтов: Онлайн-инструменты, такие как SpritePad или Stitches, позволяют загружать несколько изображений и автоматически генерировать спрайт вместе с соответствующим кодом CSS.
  • Программное обеспечение для редактирования изображений: такие программы, как Adobe Photoshop или GIMP, можно использовать для ручного создания спрайтов, упорядочивая меньшие изображения в новом файле и экспортируя результат в виде одного изображения.

Пример кода CSS: использование спрайтов изображений

Предположим, что есть изображение спрайта с именем icons.png , содержащее несколько значков. Вы можете использовать следующий CSS код для отображения отдельных значков в качестве фоновых изображений для разных элементов:

.icon < width: 32px; height: 32px; background-image: url('icons.png'); >.icon-search < background-position: 0 0; >.icon-settings < background-position: -32px 0; >.icon-user

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

Ленивая загрузка ресурсов

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

Пример кода JavaScript: реализация отложенной загрузки

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

An example image

Затем создайте скрипт, который настраивает IntersectionObserver для загрузки изображений по мере их поступления в окно просмотра (viewport):

document.addEventListener('DOMContentLoaded', function () < const lazyImages = [].slice.call(document.querySelectorAll('.lazy-load')); if ('IntersectionObserver' in window) < const lazyImageObserver = new IntersectionObserver(function (entries, observer) < entries.forEach(function (entry) < if (entry.isIntersecting) < const lazyImage = entry.target; lazyImage.src = lazyImage.dataset.src; lazyImage.classList.remove('lazy-load'); lazyImageObserver.unobserve(lazyImage); >>); >); lazyImages.forEach(function (lazyImage) < lazyImageObserver.observe(lazyImage); >); > >);

В этом примере IntersectionObserver следит за тем, чтобы изображения .lazy-load попадали в окно просмотра. При обнаружении изображения атрибуту src присваивается атрибут data-src , что приводит к фактической загрузке изображения. После загрузки образа класс .lazy-load удаляется, и наблюдение за изображением останавливается ( .unobserve() ). Используя эту простую технику отложенной загрузки, вы можете гарантировать, что загружаются только те изображения, которые в данный момент находятся в поле зрения, уменьшая количество сетевых запросов и улучшая начальное время загрузки вашего веб-сайта.

Использование кэширования

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

Кэширование браузера

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

Заголовки Cache-Control и ETag

Двумя важными заголовками для управления кэшированием браузера являются Cache-Control и ETag . Заголовок Cache-Control позволяет задать директивы кэша, такие как максимальный срок службы ресурса в кэше или необходимость его повторной проверки. Например, можно использовать Cache-Control: public, max-age=3600 , чтобы указать, что ресурс может кэшироваться в течение одного часа. Заголовок ETag предоставляет уникальный идентификатор (обычно хэш) для определенной версии ресурса. Когда браузер запрашивает ресурс, он отправляет значение ETag , которое находится в его кэше. Если значение ETag сервера совпадает со значением, отправленным браузером, сервер отвечает статусом 304 Not Modified , и браузер использует кэшированную версию. Этот механизм помогает гарантировать, что браузер всегда имеет самую последнюю версию ресурса.

Настройка кэширования на стороне сервера

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

 Header set Cache-Control "public, max-age=86400" 

Эта конфигурация задает заголовок Cache-Control для файлов CSS, JS, JPG и PNG, что позволяет кэшировать их в течение 24 часов. Используя кэширование браузера, вы можете значительно сократить объем данных, которые необходимо извлекать при повторном посещении пользователем вашего сайта, ускоряя время загрузки и улучшая общий пользовательский опыт.

Использование асинхронной загрузки

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

Асинхронная загрузка файлов JavaScript

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

Использование атрибутов Async и Defer

  • async : атрибут async указывает браузеру загрузить сценарий, не блокируя рендеринг. Как только скрипт будет загружен, браузер приостановит рендеринг для его выполнения. Это полезно для скриптов, которые не полагаются на другие скрипты или полную загрузку модели DOM.
  • defer : атрибут defer предписывает браузеру загрузить скрипт, не блокируя рендеринг, но откладывает выполнение до тех пор, пока модель DOM не будет полностью проанализирована. Это полезно для скриптов, зависящих от модели DOM или других скриптов.

Пример кода JavaScript: использование async и defer

Рассмотрим пример использования атрибутов async и defer в HTML-файле:

В этом примере main.js загружается с атрибутом defer , гарантируя, что он не будет блокировать рендеринг и будет выполнен после полного анализа DOM. Между тем, analytics.js загружается с атрибутом async , что позволяет ему загружаться и выполняться независимо от остальной части страницы. Используя асинхронную загрузку файлов JavaScript, вы можете свести к минимуму ресурсы, блокирующие рендеринг, и повысить производительность и удобство работы вашего веб-сайта.

Использование лучших практик для более быстрой загрузки и улучшения UX

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

Разделение кода

  • Webpack: Этот популярный сборщик предлагает встроенную поддержку разделения кода. Используя функцию динамического импорта import() , вы можете загружать модули JavaScript по запросу, сокращая время первоначальной загрузки.
  • React.lazy: Если вы используете React, функция React.lazy позволяет загружать компоненты лениво по мере необходимости, что еще больше оптимизирует ваше приложение.

Пример кода JavaScript: реализация разделения кода

Ниже приведен пример разделения кода с помощью Webpack и React:

import React, < lazy, Suspense >from 'react'; // Ленивая загрузка компонента с React.lazy const MyComponent = lazy(() => import('./MyComponent')); function App() < return ( 
Loading.
>>
); > export default App;

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

Использование сетей доставки контента (CDN)

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

Оптимизация манипулирования DOM и обработки событий

Эффективное манипулирование DOM имеет решающее значение для повышения производительности веб-сайта. Модель DOM (Document Object Model) представляет структуру веб-страницы, и управление ею может быть ресурсоемким. Оптимизируя код JavaScript для работы с DOM, вы можете свести к минимуму влияние на производительность и создать более плавный пользовательский интерфейс.

Пример кода JavaScript: эффективное манипулирование DOM

Ниже приведен пример оптимизации манипуляций с DOM:

// Неэффективная работа с DOM const list = document.querySelector('#list'); for (let i = 0; i < 1000; i++) < const item = document.createElement('li'); item.textContent = `Item $`; list.appendChild(item); >
// Эффективная работа с DOM const list = document.querySelector('#list'); const fragment = document.createDocumentFragment(); for (let i = 0; i < 1000; i++) < const item = document.createElement('li'); item.textContent = `Item $`; fragment.appendChild(item); > list.appendChild(fragment);

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

Инструменты разработчика браузера

Большинство современных браузеров поставляются со встроенными инструментами разработчика, которые помогут вам отслеживать и оптимизировать производительность вашего веб-сайта. Например, Chrome DevTools и Firefox Developer Tools предлагают такие функции, как профилирование производительности, мониторинг сети и отладка JavaScript. С помощью этих средств можно выявить узкие места и области, требующие улучшения в коде.

Онлайн-инструменты

  • Google PageSpeed Insights: Этот инструмент анализирует ваш веб-сайт и предоставляет предложения по повышению его производительности. Он учитывает такие факторы, как время отклика сервера, оптимизация изображений и методы загрузки JavaScript.
  • WebPageTest: WebPageTest — это комплексный инструмент тестирования производительности, который предоставляет подробную информацию о времени загрузки вашего веб-сайта, рендеринге и многом другом. Вы также можете сравнить производительность своего веб-сайта с другими сайтами или запустить тесты из разных мест и устройств.

Итоги

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

10 трендов веб-разработки в 2023 году

10 месяцев назад · 10 мин. на чтение

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

SSR фреймворки

Одностраничные приложения (SPA) и соответствующие им фреймворки (например, React.js, Vue.js, Svelte.js) прошли через более или менее громкие циклы и существуют уже много лет. Однако с появлением мета-фреймворков поверх этих решений мы можем наблюдать четкую тенденцию перехода приложений от рендеринга на стороне клиента (CSR) к рендерингу на стороне сервера (SSR). В наши дни SSR повсеместно используется при работе с JavaScript-фреймворками. Самый популярный SSR фреймворк под названием Next.js работает поверх React.js. Эндрю Кларк, core разработчик React, назвал его "настоящим релизом React 18" в 2022 году, потому что он поставляется со всеми встроенными возможностями (например, Suspense, streaming SSR), которые команда React предоставляет в качестве фундаментальных строительных блоков на нижнем уровне библиотеки. И Vercel (компания, стоящая за Next.js), и основная команда React.js работают в тесном сотрудничестве, обеспечивая отличный опыт для разработчиков.

Хотя многие разработчики смотрят на тесную связь между Next.js и React.js с опаской, для React.js есть альтернативы, такие как Remix (недавно приобретенная Shopify). Remix использует другой подход к превращению React.js в SSR-фреймворк, но есть и функции, которые схожи (например, вложенная маршрутизация) в обоих фреймворках из-за их конкуренции. Несмотря на то, что Next.js уже является признанным лидером в современной SSR разработке и превратил многих фронтенд-разработчиков в фуллстек разработчиков, другие фреймворки тоже должны быть в вашем списке внимания: SvelteKit (построенный на Svelte.js) с его недавним релизом 1.0 при поддержке Vercel и SolidStart (построенный на Solid.js) с его улучшенным DX по сравнению с React.js.

Виды рендеринга

В то время как в последнее десятилетие (2010 - 2020) доминировали одностраничные приложения (SPA) с их клиентским рендерингом (CSR), начиная с Knockout.js и Ember.js и заканчивая Angular.js, React.js и Vue.js, в последние годы наблюдается растущий интерес к рендерингу на стороне сервера (SSR) с SSR-фреймворками. Со стороны кажется, что цикл снова замыкается, потому что мы уже давно (с 2005 по 2010 год) используем SSR с внедрением JavaScript (например, jQuery, MooTools, Dojo.js) в многостраничных приложениях (MPA). Однако, если раньше использовилась Java (например, JSP) или позже Ruby on Rails, то в этот раз все по-другому, потому что вместо этого мы полагаемся на JavaScript. В течение нескольких лет Next.js был движущей силой этой тенденции, однако, другие SSR-фреймворки, такие как SvelteKit, догоняют. SSR довольно долго конкурировал со статической генерацией сайтов (SSG) за идеальную производительность, хотя оба паттерна служат совершенно разным целям. В то время как последний паттерн используется для статического контента (например, веб-сайты типа блога), первый используется для динамического контента (например, веб-приложения). Если SEO имеет значение, то и SSR, и SSG могут иметь смысл. Однако при необходимости создания динамичного контента или контента, ориентированного на пользователя, с аутентификацией, разработчики не могут выбрать SSG (один раз собрать перед развертыванием, поэтому статичен) и вынуждены выбирать между SSR (сборка по требованию для каждого запроса с индивидуальными данными на сервере) и CSR (выборка индивидуальных данных по требованию на клиенте).

Однако CSR, SSR, SSG - это не самые последние тенденции в технике рендеринга. В то время как SSR и SSG начали тенденцию оптимизации производительности несколько лет назад, появились иные техники рендеринга, такие как Incremental Static Regeneration (ISR) и Streaming SSR. ISR берет за основу SSG, поскольку позволяет статически перестраивать сайт на основе отдельных страниц (например, перестраивать страницу X каждые 60 секунд) вместо того, чтобы перестраивать весь сайт. Кроме того, On-Demand ISR, также называемая On-Demand Revalidation, может быть использована для пересборки через API (например, при обновлении данных CMS). Потоковый SSR, с другой стороны, оптимизирует однопоточное узкое место рендеринга на стороне сервера. В то время как обычный SSR должен ждать от сервера данных, чтобы отправить отрисованный контент клиенту сразу, потоковый SSR позволяет разработчикам разделить приложение на фрагменты, которые могут быть отправлены параллельно от сервера к клиенту.

В последние годы модели рендеринга были довольно прямолинейными с SSG и SSR в SPA/MPA. Однако в наши дни в моду входят более тонкие варианты. Но не только ISR и SSR стриминг становятся более актуальными, но и частичная гидратация (например, React Server Components), которая позволяет гидратировать только некоторые компоненты на клиенте, прогрессивная гидратация (Progressive Hydration), которая дает более тонкий контроль над порядком гидратации, Island Architectures (островные архитектуры) (например, Astro) для изолированных приложений или компонентов в MPA, а также использование возобновляемости вместо гидратации (например, Qwik с его SSR-фреймворком Qwik City) становятся актуальными подходами в наши дни.

Serverless (бессерверные функции)

Технологии рендеринга, такие как SSR и SSG, очень коррелируют с тенденцией serverless at the edge, потому что обе они ориентированы на производительность с целью обеспечения бесшовного пользовательского опыта в браузере. По сути, стремление обслуживать пользователей быстрее веб-сайтов и веб-приложений вызвало интерес к serverless edge. Но давайте начнем с самого начала: Бессерверность, также известная как бессерверные функции, бессерверные вычисления (например, AWS Lambda) или облачные функции (например, Google/Firebase Cloud Functions), уже несколько лет является большой тенденцией в облачных вычислениях. Хотя бессерверность по-прежнему означает наличие работающего (удаленного) сервера, разработчику не нужно управлять сервером и связанными с ним задачами (например, масштабирование инфраструктуры по требованию). Вместо этого необходимо развернуть одну функцию как бессерверную функцию, о которой позаботится облачный провайдер.

Бессерверные функции открывают еще одно преимущество, поскольку вместо развертывания сервера приложений в одном (или нескольких) центре (центрах) обработки данных, их могут быть десятки по всему миру. Поэтому в идеальном мире бессерверные функции должны работать как можно ближе к пользователям, так как это означает кратчайший путь клиент-сервер и, следовательно, улучшенный пользовательский опыт. Развертывание бессерверных функций как можно ближе к пользователю привело к появлению терминов edge computing и edge functions. Многие облачные провайдеры (например, Cloudflare с Cloudflare Workers, Vercel со своей Edge Network, Deno с Deno Deploy) конкурируют в этом пространстве, где каждый оптимизирует для достижения наилучшего времени интерактивности (TTI) для своих конечных пользователей. Пограничные функции не только быстрее обслуживают контент SSG/SSR (поскольку провод до конечного пользователя короче), но и могут кэшировать свои результаты ближе к пользователю. Но не только производительность имеет значение, хотя она и является основным фактором, другие преимущества, такие как снижение стоимости, также связаны с вычислениями, которые происходят ближе к пользователю. Например, часто не все данные, передаваемые между клиентом и сервером (здесь функция edge), должны вычисляться в главном центре обработки данных. В IoT существует множество нерелевантных данных (например, видеозаписи без изменений в каждом кадре), которые отправляются в главный центр обработки данных и могут быть просто отфильтрованы на границе. В конце концов, пограничные функции - это только начало.

Возрождение баз данных

С появлением бессерверных функций (на границе) базы данных также переживают ренессанс. При использовании бессерверных функций разработчики быстро столкнулись с проблемой открытия слишком большого количества соединений с базой данных, поскольку существует не один сервер, который держит открытым одно соединение, а множество бессерверных функций с соединением 1:1 с базой данных. Решением этой проблемы стало объединение соединений, но об этом приходится заботиться либо самостоятельно, либо с помощью стороннего сервиса. Популярными соперниками в области бессерверных баз данных являются PlanetScale (MySql), Neon (PostgreSQL) и Xata (PostgreSQL), которые имеют множество функций, таких как разветвление баз данных, дифферинцирование схем и мощный поиск/аналитика/инсайты. Когда речь заходит о бессерверных системах, они предоставляют краевое кэширование или распределенную базу данных только для чтения, чтобы переместить данные ближе к пользователям с минимальной задержкой. Если сторонний сервис должен распространять не только вашу базу данных, но и ваше приложение, Fly.io упаковывает все в одну платформу. Что выводит нас за рамки только баз данных, где также происходит много движений. Railway, рассматриваемый как преемник Heroku, предлагает все для платформы как услуги (PaaS) для развертывания вашего технологического стека. Если вы хотите продвинуться на один шаг вверх по цепочке услуг в сторону Backends as a Service (BaaS), вы получите альтернативу Firebase с открытым исходным кодом - Supabase, которая поставляется с хостингом приложений/баз данных, аутентификацией и пограничными функциями.

Среды выполнения JavaScript

Все началось с того, что Райан Дал объявил о Node.js на конференции в 2009 году. То, что начиналось как эксперимент, который отделил JavaScript от браузера и сделал его доступным на сервере, стало одним из самых больших факторов успеха JavaScript за последнее десятилетие. По сути, Райан Дал использовал JavaScript Engine под названием V8 (реализованный в Chrome) для Node.js без самого браузера. Таким образом, и браузер Chrome, и Node.js используют один и тот же JavaScript Engine, но имеют свои собственные JavaScript Runtimes (например, Browser APIs vs Node APIs) для взаимодействия с ним. Десятилетие спустя Райан Дал объявил Deno преемником Node с обещанием предоставить разработчикам более безопасную и быструю среду, которая поставляется с похожими API браузера, TypeScript и стандартной библиотекой из коробки. Однако Deno, который также работает на V8, является лишь одним из многих JavaScript Runtimes в наши дни.

В конкурирующей стране краевых функций многие облачные провайдеры внедряют свои собственные JavaScript Runtime (например, Cloudflare Workers), которые оптимизированы для их собственной инфраструктуры (например, Cloudflare). Таким образом, бизнес-модель Deno также становится облачным провайдером с Deno Deploy и их SSR-фреймворком для рендеринга на границе (который начинался как доказательство концепции) под названием Deno Fresh. Независимые от облачных провайдеров решения, такие как Bun (работающий на JavaScriptCore Engine), недавно стали еще одним вирусным хитом в гонке за самый быстрый JavaScript Runtime. Если что-то пойдет не так, мы окажемся в той же ситуации, что и с фрагментированной поддержкой JavaScript в браузерах в течение многих лет, но на этот раз на сервере, где не весь JavaScript может поддерживаться одинаково во всех средах выполнения при развертывании на различных облачных провайдерах. Поэтому все заинтересованные стороны (например, Deno, Vercel, Cloudflare) присоединились к WinterCG, чтобы сотрудничать в области совместимости API между их JavaScript Runtimes.

Монорепо

В прошлом монорепо использовалось в основном для крупномасштабных приложений, где один проект содержит более мелкие проекты в одном репозитории с контролем версий. Каждый из этих небольших проектов может быть чем угодно - от отдельного приложения (например, SPA, MPA) до пакета многократного использования (например, функции, компоненты, сервисы). Практика объединения проектов восходит к началу 2000 годов, когда это называлось общей кодовой базой. Однако в наши дни монорепозитории используются не только крупными приложениями, но и небольшими компаниями и проектами с открытым исходным кодом, которые, несомненно, извлекут из них пользу. Например, компания может иметь различные пакеты в монорепозитории, начиная от общих компонентов пользовательского интерфейса, общей системы проектирования (например, многоразовое совместное проектирование) и заканчивая широко используемыми полезными функциями для соответствующей области.

Эти пакеты могут быть импортированы в различные приложения: реальное приложение (например, app.mywebsite.com с рендерингом на стороне клиента), которое использует все эти общие пакеты, домашняя/продуктовая/лендинговая страница (например, mywebsite.com с рендерингом на стороне сервера или генерацией статического сайта) с учетом SEO использует только пакет общей системы дизайна, а страница технической документации (например, docs.mywebsite.com) использует общие компоненты пользовательского интерфейса и пакеты общей системы дизайна. Turborepo (приобретенная компанией Vercel) вновь подняла шумиху о монорепо в JavaScript/TypeScript. Turborepo позволяет командам создавать конвейеры сборки для всех своих приложений и пакетов в рамках монорепо. Самое привлекательное: кэширование сборок в рамках конвейера на локальной машине или в облаке для всех команд. Turborepo в сочетании с другими жизненно важными инструментами монорепо, такими как npm/yarn/pnpm workspaces (управление зависимостями) и changesets (версионирование), делает этот инструментарий местом, за которым стоит следить в этом году. Конкурентами Turborepo являются Nx, Rush и Lerna (некоторое время не поддерживался, затем был приобретен компанией Nx - Nrwl).

CSS на утилитах

Разработчики либо любят, либо ненавидят его: Tailwind CSS является примером CSS с утилитами. В то время как одна сторона разработчиков ненавидит его за многословность в коде пользовательского интерфейса, другая сторона разработчиков любит его за его великолепный DX. Как разработчик, вы настраиваете его один раз в своем проекте и сразу же используете его готовый CSS в HTML. Этому разделению любви и ненависти к CSS, ориентированному на утилиты, может прийти конец с недавним ростом рендеринга на стороне сервера (SSR). В течение нескольких лет CSS-in-JS решения, такие как Styled Components (SC) и Emotion, были преобладающей силой для стилизации современных веб-приложений на основе компонентов. Однако, если производительность в мире SSR является одной из основных целей, CSS-in-JS имеет негативные последствия: увеличение размера пакета (SC - 12,7 кБ, Emotion - 7,9 кБ) и, что более важно, накладные расходы во время выполнения из-за сериализации CSS перед вставкой в DOM.

Поэтому мы можем наблюдать миграцию разработчиков в сторону более дружественных SSR решений, таких как utility-first-CSS (например, Tailwind CSS, UnoCSS) в паре с предварительно определенным UI компонентом (например, DaisyUI), других не менее популярных альтернатив, таких как CSS Modules, или так называемых без ранатаймовых (без компиляции) CSS-in-JS (например, vanilla-extract, linaria, astroturf, compiled).

TypeScript

Эволюция от JavaScript к TypeScript неостановима. В этой большой миграции веб-разработки безопасность типов E2E для фулстек приложений, безусловно, является важной тенденцией. Реализация этой концепции зависит от коммуникационного уровня (API), который необходим для передачи типизированных сущностей (например, тип User, тип BlogPost) от сервера к клиентскому приложению. Обычными подходами в веб-разработке для связи клиент-сервер являются REST и GraphQL. Оба этих языка можно использовать с OpenAPI для REST и GraphQL Code Generator для GraphQL для создания типизированного файла схемы для фронтенд приложения. Однако появилась новая восходящая звезда типобезопасных API под названием tRPC, которая может быть использована в качестве замены REST/GraphQL. Если вы работаете в монорепо TypeScript, где фронтенд и бэкенд совместно используют код, tRPC позволяет экспортировать все типы из бэкенда во фронтенд-приложение без промежуточной генерации типовой схемы. Впоследствии фронтенд может вызывать API бэкенда, просто используя типизированные функции, которые под капотом соединены HTTP для обеспечения фактического взаимодействия между клиентом и сервером. Общая тенденция, безусловно, направлена на использование большего количества таких типобезопасных решений для фулстек приложений, таких как tRPC, Zod, Prisma и TanStack Router, которые обеспечивают типобезопасность приложения.

Инструменты сборки

В React несколько лет доминировал create-react-app (CRA). В свое время это была небольшая революция, поскольку начинающие разработчики получали готовый стартовый проект React без необходимости настраивать Webpack с React. Однако за последний год Webpack довольно быстро устарел. Vite - это новый инструмент в блоке, когда речь идет об одностраничных приложениях (SPA), поскольку он работает со всеми популярными фреймворками (например, React.js) для создания стартового проекта. Разработанный Эваном Ю, создателем Vue.js, он называет себя фронтенд-инструментом нового поколения. Под капотом он получает свою мощь от esbuild, который по сравнению с другими JavaScript бандлерами написан на Go, и поэтому собирает зависимости в 10-100 раз быстрее, чем его конкуренты (например, Webpack). В то время как экосистема Vite процветает благодаря таким дополнениям, как Vitest (альтернатива Jest для тестирования), совсем недавно появились другие конкуренты, такие как Turbopack от Vercel. Turbopack называют преемником Webpack, потому что его создателем является Тобиас Копперс, создатель Webpack. Поскольку Next.js все еще использует Webpack, а Turbopack разработан той же компанией, мы можем ожидать, что Next.js и Turbopack будут идеально сочетаться в будущем.

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

Заменит ли ИИ в конечном итоге работу разработчика? На этот вопрос пока нет ответа, однако разработка с использованием ИИ стала реальностью в 2022 году. С выходом GitHub Copilot разработчики получили возможность работать в паре с ИИ-программистом в своей любимой IDE. Это так же просто, как написать код (или написать комментарий с указанием того, что вы хотите написать), и GitHub Copilot автоматически заполнит детали реализации в соответствии со своим пониманием. Но на этом все не заканчивается: ChatGPT от OpenAI - это более общая языковая модель, которая также заботится о задачах программирования. Хотя вы можете задавать ChatGPT вопросы в свободной форме, он также способен выполнять задачи по кодированию. Многие разработчики уже заметили, что используют ChatGPT в качестве замены StackOverflow. Во многих ситуациях ChatGPT давал полезные ответы (хотя и не всегда безупречные), когда использовался в качестве замены поисковой системы. Поскольку последнему приходится иметь дело с большим количеством SEO-спама (не только для контента, связанного с разработкой), ChatGPT рассматривается как жизнеспособная альтернатива на данный момент. Для того чтобы правильно формулировать и задавать запросы в системе ChatGPT для получения наиболее точных и информативных ответов, рекомендую прочитать Как написать запрос к ChatGPT. От промпта к результату.

Однако "в настоящее время" - это важный термин. С высоты птичьего полета созданный искусственным интеллектом контент может (и будет) также вредить всемирной паутине. Если раньше созданный вручную SEO-контент уже был проблемой, то теперь никто не мешает кому-то производить больше автоматически генерируемого SEO-контента с помощью ChatGPT. Будет ли ChatGPT в конечном итоге тренироваться на собственном генерируемом контенте?

Ещё

Есть несколько заслуживающих внимания упоминаний, но которые не попали в список трендов: Tauri как альтернатива Electron для настольных приложений, реализованных на JavaScript/CSS/HTML, Playwright как альтернатива Cypress для E2E тестирования, Warp и Fig как терминалы нового поколения, CSS Container Queries как альтернатива CSS Media Queries для отзывчивого дизайна, и, наконец, htmx как обогащенный HTML для создания интерактивных пользовательских интерфейсов без JavaScript.

Добавить комментарий

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