Pull to refresh

Объединение JS-файлов 2.0 (1/2)

Website development *
В последнее время стало модно объединять все внешние JavaScript-файлы вашего сайта в один большой, загружаемый один раз и навсегда. Это, скажем прямо, хорошо — браузер не делает сто миллионов запросов на сервер для отображения одной страницы 1, скорость загрузки повышается, пользователи счастливы, разработчики отдыхают.
Как всегда, в бочке мёда есть ложка дёгтя — в объединённый файл в этом случае попадает много того, что при первом запросе можно было бы и не загружать.2 Здесь должна была быть ссылка на хабратопик с соответствующим обсуждением. Успешно потеряна. Чаще всего для борьбы с этим предлагают выкидывать ненужные части руками… Лично у меня перспектива каждый раз перелопачивать несколько десятков (а то и сотен 3) килобайт JavaScript кода вызывает острое нежелание работать — а у вас?
под катом: описание простейшего алгоритма разрешения зависимости между модулями

Конструктивные предложения


Предложение первое: разобрать используемый вами фреймворк на составные части. JSON — отдельно, AJAX — отдельно, работа с DOM — отдельно, формы — отдельно. После этого задача «выкидывания ненужного» превращается в задачу «собери только нужное». Несомненный плюс — результат сборки стал меньше. Несомненный минус — если что-то из «нужного» забыто, все перестаёт работать.
Предложение второе: сохранить информацию о зависимостях между составными частями. (Формы используют функции DOM, JSON — AJAX и так далее.) На этом шаге забыть что-то нужное становится заметно труднее, а сборка превращается из увлекательной головоломки "...@#$, почему всё перестало работать..." в рутинную и нудную операцию.
Предложение третье: сохранить информацию о том, какие именно модули нужны сайту в целом. Используется ли AJAX? Если ли формы? Какие-то необычные элементы управления?
Предложение четвёртое: подумать, и заставить работать машину.

Теория


С формальной точки зрения, после того, как первый и второй шаг выполнены, у нас появляется дерево4 зависимостей. Например… (не стреляйте в пианиста — пример высосан из пальца)
- dom.js
  - array.map.js
    - array.js
  - sprinf.js
- calendar.js
  - date.js
  - mycoolcombobox.js
    - dom.js
      - array.map.js
        - array.js
      - sprinf.js
- animated.pane.js
  - pane.js
    - dom.js
      - array.map.js
        - array.js
      - sprinf.js
  - animation.js
    - transition.js
... и так далее ...

На третьем шаге мы выбираем непосредственно нужные сайту вершины. Пусть это будут dom.js и animated.pane.js.
Теперь дело техники обойти получившийся набор деревьев в глубину
- array.js
- array.map.js
- sprinf.js
- dom.js
- array.js
- array.map.js
- sprinf.js
- dom.js
- pane.js
- transition.js
- animation.js
- animated.pane.js

… удалить повторяющиеся элементы:
- array.js
- array.map.js
- sprinf.js
- dom.js
- pane.js
- transition.js
- animation.js
- animated.pane.js

и слить соответствующие модули воедино.

Немножко практики


Как хранить информацию о зависимостях?


Лично я предпочитаю добавлять в «модули» служебные комментарии:
// #REQUIRE: array.map.js
// #REQUIRE: sprintf.js
....
код

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

К чему мы пришли?


Затратив один раз кучу времени на формирование модулей и зависимостей между ними, мы экономим время каждый раз, когда хотим уменьшить объем загружаемого клиентов внешнего файла. Приятно. Но всё-таки часть проблемы осталась — пользователь загружает весь JS-код, который используется на сайте за раз, даже если на текущей странице этот код не нужен.
(Продолжение следует...)
1 При правильно настроенном кэшировании (наличии 'Expires') эти запросы будут отправлены только при первой загрузке страницы. Тем не менее, встречают по одёжке.
2 Например, весь Prototype целиком, вместо отдельно взятых функций '$' и '$$', ага.
3 Вы не пользуетесь JS-фреймворком (пусть даже велосипедом собственного изобретения)?
4 На самом деле не дерево, а DAG (Направленный Ациклический Граф) — я просто не знаю правильного русскоязычного термина. Ах да, если у вас в зависимостях получились циклы — что-то где-то было разбито неправильно.
Tags:
Hubs:
Total votes 34: ↑29 and ↓5 +24
Views 2.9K
Comments Comments 70