CondiLoader — условная асинхронная загрузка скриптов и CSS

Я уже не раз писал в этом блоге о приемах для ускорения отображения сайтов. Это и отложенная асинхронная загрузка, и условная загрузка по имени класса, и запуск inline JavaScript-кода после загрузки внешнего файла по событию.
Теперь же я сделал библиотеку-загрузчик, которая упрощает и автоматизирует их применение, а также позволяет собрать список всех используемых на сайте библиотек в одном месте. Называется она CondiLoader, скачать ее можно на GitHub или же установить через npm:
npm install condiloader
Работает CondiLoader следующим образом: вы передаете скрипту список объектов для загрузки, он дожидается события DOMContentLoaded. После этого для каждого объекта проверяет, необходимо ли загружать его для данной страницы. Если да, приступает к загрузке, после чего вызывает функцию инициализации, либо пользовательское событие с указанным именем. Все объекты загружаются параллельно, и ошибка при загрузке одного из них на остальные не влияет.
У каждого объекта могут быть следующие свойства:
  • sel —  селектор CSS. С помощью него проверяется, есть ли такие элементы на странице. Если они найдутся, то текущий объект обрабатывается, если нет — пропускается.
  • xpath — делает то же самое, что и sel, но для проверки указывается не селектор CSS, а выражение XPath.
  • css — массив с именами CSS-файлов для загрузки. Если файл один, его можно указать просто как строку.
  • js — массив с именами JavaScript-файлов для загрузки. Если файл один, его можно указать просто как строку. При загрузки файлов, относящихся к одному элементу, порядок из загрузки сохраняется.
  • init — функция, которая будет вызвана после загрузки всех необходимых файлов.
  • event — имя пользовательского события, которое будет вызвано после того, как все файлы будут загружены и отработает функция init
  • name — имя блока для удобства отладки.
Ни один из этих параметров не является обязательным. Если не задан ни sel, ни xpath, блок будет обрабатываться в любом случае, если оба — то они объединяются по условию И.
По умолчанию библиотека отслеживает и предотвращает повторную загрузку одного и того же JavaScript-файла, однако это можно выключить.
Предположим, что у нас имеется сайт, на некоторых страницах которого есть «крутилка» с баннерами (banner carousel), а на некоторых — input с классом date, где нужно сделать выбор даты с помощью скрипта.
Тогда в шаблоне сайта можно прописать такой код:
<script> function CondiLoaderInit() {  new CondiLoader([ // первый объект — carousel для баннеров { sel: ".carousel", // блок будет обработан только в том случае, если на странице есть элемент с классом carousel   js: ["jquery.js","jquery.carousel.js"], // загружаем jQuery и соответствующий plugin. Порядок загрузки будет соблюдаться   css: "carousel.css",  // загружаем стили   init: ()=>jQuery('.carousel').carousel(), // задаем функци, которая будет запущена после окончания всех загрузок, именно она добавит carousel к нужному элементу   event: "CarouselLoaded", // пользовательское событие, которое будет вызвано после того, как карусель готова   name: "Carousel block" // имя блока для отладки }, // { sel: ".date", // блок будет обработан только в том случае, если на странице есть элемент с классом carousel   js: ["jquery.js","jquery.calendar.js"], // загружаем jQuery и соответствующий plugin. Порядок загрузки будет соблюдаться   css: "calendar.css",  // загружаем стили   init: ()=>jQuery('.date').calendar({/* какие-то опции*/}), // задаем функцию инициализации календаря   name: "Календарь" // имя блока для отладки }  ],{ /* options */ }); } </script> <script src="condi_loader.js" onload="CondiLoaderInit()" defer="defer"></script>
Главный плюс такого подхода — код инициализации одинаков для всего сайта, не нужно помнить, на каких страницах есть баннеры, а на каких — поле ввода даты. Но при этом не будет и «загрузки на всякий случай» (что часто наблюдается во многих CMS): файлы соответствующих plugins будут загружаться только тогда, когда они действительно необходимы.
При этом файлы, относящиеся к разным блокам будут грузиться параллельно, а не последовательно, что уменьшит время полной загрузки сайта.
При необходимости можно вынести содержимое функции CondiLoaderInit в отдельный файл. Тогда код его подключения будет выглядеть так:
<script src="condi_loader.js" defer="defer"></script> <script src="condi_loader_init.js" defer="defer"></script>
При инициализации CondiLoader вторым аргументом можно указать массив с дополнительнями опциями. Они могут быть такие:
  • baseCSS — базовый путь для CSS-файлов. По умолчанию — пустой, применяется только к относительным URL.
  • baseJS — базовый путь для JavaScript-файлов. По умолчанию — пустой, применяется только к относительным URL.
  • skipDoubleLoad — если true, все JavaScript-файлы загружаются только один раз. (В примере выше он должен быть true. В противном случае если на странице будут и календарь, и баннеры, jQuery загрузится дважды, что вызовет проблемы.)
При необходимости можно создать несколько экземпляров CondiLoader, но делать это нежелательно, так как проверка двойной загрузки JavaScript производится только в пределах одного экземпляра.