Клиентская оптимизация: вынесение кода в post load на примере плагина jquery prettydate
В интернете сейчас много говорится о скорости загрузки. Не буду повторяться о том, насколько эта проблема актуальна.
Введение
Речь пойдет об оптимизации страницы на стороне клиента. Один из рабочих методов – вынесение всего, что только можно, в событие post load
. Напомню, что событие load
срабатывает после того, как загрузится вся страница полностью (включая картинки и мультимедиа).
Здесь стоит выделить необходимое условие для того, чтобы можно было выносить код в post load
:
Страница обязана корректно функционировать без кода, который будет загружаться в post load
.
Рассмотрим плагин jquery prettydate. Его основное назначение – преобразовать даты вида «4 Марта 2010» в приятные глазу «Вчера» и т.д. Легко заметить, что при нормальной разметке, когда задана дата по умолчанию, скрипт удовлетворяет необходимому условию.
Локализация расширения
Стандартным подходом к локализации практически всех jquery плагинов, что я видел, является дополнительная загрузка файла с переводом для определенного языка. На мой взгляд, у этого подхода есть ряд весомых недостатков:
- один дополнительный запрос на загрузку language-specific файла;
- неочевидно, как синхронизировать локализованные строки, если они используются и на стороне сервера.
Однако есть простое решение: возвращать не js файлы, а один цельный html контейнер. Для случая с prettydate (используется JSP, но синтаксис простой и, скорее всего, легко реализуется для других платформ):
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
Если запросить эту страницу и добавить сгенерированный html к body документа, то получим работающий плагин.
Еще одно удобство html – это его универсальность. Если плагин требует дополнительно к скриптам css или html, то проблем нет – просто добавляем нужный код в соответствующий тэг, по аналогии с script.
Инициализация расширения
Если попытаться вызвать функцию prettyDate
до того, как скрипт плагина будет получен с сервера, возникнет ошибка – функция не существует. Чтобы это исправить, воспользуемся шаблоном прокси. Алгоритм:
- Инициализируется прокси для того, чтобы определить функцию аддона (в нашем примере эта функция –
prettyDate
), однако с другим поведением; - Клиентский код вызывает функцию аддона (в нашем примере это
prettyDate
); - Прокси добавляет запрос на инициализацию аддона (в нашем примере это
prettydate
) и коллбэк с текущимим параметрами; - Когда срабатывает
load event
, посылаются ajax запросы на получение всех аддонов, которые были запрошены прокси; - После инициализации каждый аддон переопределяет свои функции (в данном случае
prettyDate
) а так же выполняет все свои коллбэки.
Реализация
Реализация алгоритма внутри функции. Важные моменты, которые следует упомянуть:
- если селектор, который вызывает функцию аддона пустой, то подгружать аддон не нужно;
- если функция аддона вызывается уже после того, как сработал load event, то необходимо сразу посылать ajax call за необходимым кодом.
(function(mappings, urls) { var addons = {}, // map of required addons windowLoaded = false; function loadAddon(name, callbacks) { $.ajax({ type: 'GET', url: urls[name], cache: true, success: function(html) { $('body').append(html); $.each(callbacks, function() { this.call(); }); // destroy all listeners addons[name] = null; } }); } // setup proxies $.each(mappings, function(funcName, addonName) { $.fn[funcName] = function() { var args = arguments, callback = $.proxy(function() { // here will be called a real function this[funcName].apply(this, args); }, this); // skip empty selectors if (this.length > 0) { if ($.isArray(addons[addonName])) { // already have binded load event addons[addonName].push(callback); } else { if (windowLoaded) { // page is already loaded so just need to make an ajax call loadAddon(addonName, [ callback ]); } else { // put addon call into query that will execute when page will be loaded addons[addonName] = [ callback ]; } } } return this; } }); // setup on load event $(window).load(function() { windowLoaded = true; $.each(addons, loadAddon); }); })({ // pattern is "functionName : addonName" 'prettyDate': 'prettydate', ... }, { // pattern is "addonName : addonUrl" 'prettydate': 'http://example.com/addon/prettydate-1.0.html', ... });
Заключение
При грамотном использовании вынесение кода в post load позволяет очень благоприятно влиять на скорость загрузки страницы. Это касается как ее размера, так и скорости рендеринга.
P.S. И конечно же не стоит пренебрегать сжатием и кэшированием html-файлов аддонов.