Joomla 2.5: Оптимизация js и Асинхронная загрузка JavaScript

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

Построение шаблона

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

Если в Вашем шаблоне загрузка css и javascript выглядит следующим образом:

<!DOCTYPE html>
<html>
<head>
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
<jdoc:include type="head" />
<link rel="stylesheet" href="/templates/beez_20/css/style.css" type="text/css" />
<script src="/templates/beez_20/js/jquery-latest.min.js" type="text/javascript"></script>
</head>

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

А вот как правильно включать файлы в рендеринг:

<?php
defined( '_JEXEC' ) or die( 'Restricted access' );
$this->addStyleSheet($this->baseurl.'/templates/'.$this->template.'/css/style.css');
$this->addScript($this->baseurl.'/templates/'.$this->template.'/js/jquery-latest.min.js');
?>
<!DOCTYPE html>
<html>
<head>
<jdoc:include type="head" />
</head>

Не обращаем внимание на валидность doctype и html, писал сокращенно для примера.

Асинхронная загрузка

А вот тут я бы больше заострил внимание и дочитал статью до конца! Часто заказчик на оптимизацию требует реализовывать асинхронную загрузку javascript. Как ни странно разработчики Joomla предусмотрели и такую возможность, а вот разработчики сторонних компонентом, модулей и плагинов забывают про данный функционал, видимо поэтому Jooml'у часто и критикуют за низкую оптимизацию, с чем бы я и поспорил.

Как рассматривалось ранее в рендеринге документа существует функция addScript();. Но видимо некоторые и не подозревают что она имеет четыре переменные - $url, $type, $defer, $async.

Если асинхронная загрузка скриптов нам не нужна достаточно использовать один атрибут $url, остальные подставляются по умолчанию ($type = "text/javascript", $defer = false, $async = false)

$this->addScript($this->baseurl."/templates/beez_20/js/jquery-latest.min.js");
$this->addScript($this->baseurl."/templates/beez_20/js/script.js");

Но в нашем случае нам необходимо загрузку скриптов браузером поставить в очередь, для этого необходимо использовать атрибут async и/или defer. Оба отличаются лишь тем что defer сохраняет порядок выполнения скриптов. Defer нужен в том случае если script.js используется фреймворк jQuery, который предотвратит его загрузку раньше самой библиотеки, ну и если необходимо сохранить некую последовательность.

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

$this->addScript($this->baseurl."/templates/beez_20/js/jquery-latest.min.js", "text/javascript");
// или
$this->addScript($this->baseurl."/templates/beez_20/js/jquery-latest.min.js", "text/javascript", false, false);
// Выводит <script src="/templates/beez_20/js/jquery-latest.min.js" type="text/javascript"></script>

$this->addScript($this->baseurl."/templates/beez_20/js/jquery-latest.min.js", "text/javascript", true, false);
// Выводит <script src="/templates/beez_20/js/jquery-latest.min.js" type="text/javascript" defer="defer"></script>

$this->addScript($this->baseurl."/templates/beez_20/js/jquery-latest.min.js", "text/javascript", false, true);
// Выводит <script src="/templates/beez_20/js/jquery-latest.min.js" type="text/javascript" async="async"></script>

И еще один маленький моментик. В принципе данные атрибуты это элементы HTML5, следовательно нет смысла использовать вид async="async" и defer="defer", достаточно async и defer соответственно.

Внесем коррективы в библиотеку joomla. Открываем сайт.ру/libraries/joomla/document/html/renderer/head.php

Ищем (примерно строка 150):

// Generate script file links
foreach ($document->_scripts as $strSrc => $strAttr)
{
	$buffer .= $tab . '<script src="' . $strSrc . '"';
	if (!is_null($strAttr['mime']))
	{
		$buffer .= ' type="' . $strAttr['mime'] . '"';
	}
	if ($strAttr['defer'])
	{
		$buffer .= ' defer="defer"';
	}
	if ($strAttr['async'])
	{
		$buffer .= ' async="async"';
	}
	$buffer .= '></script>' . $lnEnd;
}

заменяем на:

// Generate script file links
foreach ($document->_scripts as $strSrc => $strAttr)
{
	$buffer .= $tab . '<script src="' . $strSrc . '"';
	if (!is_null($strAttr['mime']))
	{
		$buffer .= ' type="' . $strAttr['mime'] . '"';
	}
	if ($strAttr['defer'])
	{
		$buffer .= ' defer';
	}
	if ($strAttr['async'])
	{
		$buffer .= ' async';
	}
	$buffer .= '></script>' . $lnEnd;
}

Вот и вся соль. Все манипуляции мной проводились в Joomla 2.5, как это будет работать в 1.5 хз, не рассматривал, да и пора уже забыть про нее ))). Да и вышеописанная техника совсем не панацея, к каждому нужен индивидуальных подход.

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

  • Спасибо огромное!!!

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

    я добавил пару поправок и все заработало

    Пример: $document->addStyleSheet($this->baseurl.'/templates/casting.content/css/template.css');

    А так Автору огромное спасибо что натолкнул на правильную мысль
  • job
    Верно! А вообще внесу коррективы в статью, хорошо что напомнили ))). Сам то я давно уже использую такую конструкцию:
    $this->addStyleSheet($this->baseurl.'/templates/'.$this->template.'/css/template.css');
    и незачем подключать класс JFactory::getDocument(), он уже подгружен ядром.
  • Извините, а если "$this->..." добавить туда где контент в joomla (при отключенной фильтрации), то скрипт и css добавятся? Я не шибко разбираюсь... Если не добавятся, не подскажите как сделать? Мне надо, чтобы на разных страницах разные скрипты подключались и css, а то все добавлять на все страницы, там и так тормозит всё... :(
  • job
    Нет не добавится. В таких случаях или в шаблоне прописать правило для определенного Itemid подключать определенный css, либо воспользоваться плагином подключающий в тело контента локальный (на сервере) php скрипт, где прописываете какой то код и так же можете подключить css Есть замечательный плагин RD_AddPhp которым я всегда пользуюсь в тех случаях когда фильтры джомлы не дают добавить свои скрипты. Так же делал локализацию для j25, посмотреть можно [url="http://slovu.net/coding/joomla/1041-rd-addphp-for-joomla-1-7-plagin-vstavki-v-statyu-php-koda"]тут[/url]
  • Асинхронная загрузка скриптов Джумла 2,5 вирталмарт 2.0.6 иду сюда libraries\joomla\document\document.php на строке 461 код public function addScript($url, $type = "text/javascript", $defer = false, $async = false) { $this->_scripts[$url]['mime'] = $type; $this->_scripts[$url]['defer'] = $defer; $this->_scripts[$url]['async'] = $async; return $this; } меняю на public function addScript($url, $type = "text/javascript", $defer = true, $async = false) { $this->_scripts[$url]['mime'] = $type; $this->_scripts[$url]['defer'] = $defer; $this->_scripts[$url]['async'] = $async; return $this; Скрипты грузятся асинхронно но вместе с этим приходят проблемы. Не добавляется товар в корзину в internet explorer и мозиле. В опере всё работает нормально. Помогите разобраться.
  • Похоже скрипт запускается раньше чем загружена сама js библиотека. можно скрипт отвечающий за добавление товара обернуть в $(window).load(function(){ ....... })(jQuery); т.е. скрипт начнет работать после полной загрузки страницы. Пробуйте...
  • Пошел пробовать заранее спасибо. А то все только стебутся, что видео курсов обсмотрелся. Позже отпишусь.
  • "И так что бы включить асинхронную загрузку необходимо использовать все переменные". Так и что именно вставлять то? Я вставил только $this->addScript($this->baseurl."/templates/beez_20/js/jquery-latest.min.js", "text/javascript", true, false);
  • job
    Решать Вам, с каким параметром добавлять скрипт на загрузку. async - выполняется независимо от загрузки страницы, так сказать параллельно без зависимостей друг от друга. defer - откладывает выполнение до тез пор пока страница не загрузится полностью.
  • Здравствуйте. У меня joomla 2.5.11 А подключение скриптов совсем иное: в index.php require_once(dirname(__FILE__) . '/php/_code.php'); ?> в /php/_code.php строчка require_once(dirname(__FILE__) . DS . 'template.php'); в template.php вот такое: private $_mootoolsScripts = array( '/media/system/js/core.js'=> array( 'async' => true, ), '/media/system/js/core-uncompressed.js', '/media/system/js/mootools-core.js', '/media/system/js/mootools-core-uncompressed.js', '/media/system/js/caption.js', '/media/system/js/caption-uncompressed.js', '/media/system/js/mootools-more.js', '/media/system/js/mootools-more-uncompressed.js' ); async не работает. Когда пытаюсь просто удалить один из скриптов - обязательно что-то на сайте перестает работать как надо. Т е похоже, все скрипты используются. Вопрос, как в данном случае прописать асинхронную загрузку? Заранее спасибо за помощь..