В последнее время стало модно объединять все внешние JavaScript-файлы вашего сайта в один большой, загружаемый один раз и навсегда. Это, скажем прямо, хорошо — браузер не делает сто миллионов запросов на сервер для отображения одной страницы 1, скорость загрузки повышается, пользователи счастливы, разработчики отдыхают.
Как всегда, в бочке мёда есть ложка дёгтя — в объединённый файл в этом случае попадает много того, что при первом запросе можно было бы и не загружать.2 Здесь должна была быть ссылка на хабратопик с соответствующим обсуждением. Успешно потеряна. Чаще всего для борьбы с этим предлагают выкидывать ненужные части руками… Лично у меня перспектива каждый раз перелопачивать несколько десятков (а то и сотен 3) килобайт JavaScript кода вызывает острое нежелание работать — а у вас?
под катом: описание простейшего алгоритма разрешения зависимости между модулями
Предложение первое: разобрать используемый вами фреймворк на составные части. JSON — отдельно, AJAX — отдельно, работа с DOM — отдельно, формы — отдельно. После этого задача «выкидывания ненужного» превращается в задачу «собери только нужное». Несомненный плюс — результат сборки стал меньше. Несомненный минус — если что-то из «нужного» забыто, все перестаёт работать.
Предложение второе: сохранить информацию о зависимостях между составными частями. (Формы используют функции DOM, JSON — AJAX и так далее.) На этом шаге забыть что-то нужное становится заметно труднее, а сборка превращается из увлекательной головоломки "...@#$, почему всё перестало работать..." в рутинную и нудную операцию.
Предложение третье: сохранить информацию о том, какие именно модули нужны сайту в целом. Используется ли AJAX? Если ли формы? Какие-то необычные элементы управления?
Предложение четвёртое: подумать, и заставить работать машину.
С формальной точки зрения, после того, как первый и второй шаг выполнены, у нас появляется дерево4 зависимостей. Например… (не стреляйте в пианиста — пример высосан из пальца)
На третьем шаге мы выбираем непосредственно нужные сайту вершины. Пусть это будут dom.js и animated.pane.js.
Теперь дело техники обойти получившийся набор деревьев в глубину
… удалить повторяющиеся элементы:
и слить соответствующие модули воедино.
Лично я предпочитаю добавлять в «модули» служебные комментарии:
Выделить подобные метки из текстового файла не составляет труда.
Естественно, чтобы получить полное дерево зависимостей, надо будет пройтись по всем доступных файлам — но полное дерево обычно не нужно.
Затратив один раз кучу времени на формирование модулей и зависимостей между ними, мы экономим время каждый раз, когда хотим уменьшить объем загружаемого клиентов внешнего файла. Приятно. Но всё-таки часть проблемы осталась — пользователь загружает весь JS-код, который используется на сайте за раз, даже если на текущей странице этот код не нужен.
(Продолжение следует...)
1 При правильно настроенном кэшировании (наличии 'Expires') эти запросы будут отправлены только при первой загрузке страницы. Тем не менее, встречают по одёжке.
2 Например, весь Prototype целиком, вместо отдельно взятых функций '$' и '$$', ага.
3 Вы не пользуетесь JS-фреймворком (пусть даже велосипедом собственного изобретения)?
4 На самом деле не дерево, а DAG (Направленный Ациклический Граф) — я просто не знаю правильного русскоязычного термина. Ах да, если у вас в зависимостях получились циклы — что-то где-то было разбито неправильно.
Как всегда, в бочке мёда есть ложка дёгтя — в объединённый файл в этом случае попадает много того, что при первом запросе можно было бы и не загружать.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 (Направленный Ациклический Граф) — я просто не знаю правильного русскоязычного термина. Ах да, если у вас в зависимостях получились циклы — что-то где-то было разбито неправильно.