#[Larakit Staticfiles] Библиотека для управления статикой (сборка в один файл, сжатие JS/CSS, добавление хэша в URL для сброса кэша браузера)
Модуль был портирован с модуля для фреймворка Kohana, который служил мне верой и правдой более 5 лет ( и модуль, и Kohana :) ) (подробнее я описывал его тут https://habrahabr.ru/post/112852/)
Примечание: будем считать, что проект Laravel у вас уже создан
###1. Файл с правилами подключения статики
Для порядка добавим подключаемые стили и скрипты вынесем в отдельный файл, например
./app/Http/staticfiles.php
Затем в файле
./app/Http/routes.php
подключим его
<?php
Route::get('/', function () {
return view('welcome');
});
require app_path('Http/staticfiles.php');
###2. Установим пакет
$composer require larakit/lk-staticfiles
Далее нужно либо воспользоваться рекомендациями пакета https://github.com/larakit/lk-boot и произвести две правки
./app/Http/Kernel.php
и
./config/app.php
либо руками зарегистрировать в
./config/app.php
сервис-провайдер "Larakit\StaticFiles\LarakitServiceProvider"
<?php
return [
...
'providers' => [
...,
Larakit\StaticFiles\LarakitServiceProvider::class
],
...
];
Проверим что все хорошо, для этого наберем в консоли команду:
php artisan | grep larastatic
Если вы видите текст:
larastatic
larastatic:deploy Выложить статику из зарегистрированных пакетов в DOCUMENT_ROOT
поздравляю, пакет был корректно установлен и инициализирован!
###3. Заполним staticfiles.php инструкциями по подключению CSS
Для начала посмотрим содержимое по-умолчанию главной страницы после установки фреймворка
и постараемся воспроизвести его при помощи данного пакета
<?php
\Larakit\StaticFiles\Css::instance()
->add('https://fonts.googleapis.com/css?family=Lato:100')
->addInline('
html, body {
height: 100%;
}
body {
margin: 0;
padding: 0;
width: 100%;
display: table;
font-weight: 100;
font-family: "Lato";
}
.container {
text-align: center;
display: table-cell;
vertical-align: middle;
}
.content {
text-align: center;
display: inline-block;
}
.title {
font-size: 96px;
}
');
Чтобы добавленные стили и скрипты вставились во все страницы сайта надо в вашем шаблоне прописать вызов
Получилось!
Примечание: результаты выполнения
\Larakit\StaticFiles\Css::instance()
->add('"https://fonts.googleapis.com/css?family=Lato:100')
->addInline('
html, body {
height: 100%;
}
');
и
\Larakit\StaticFiles\Css::instance()
->add('"https://fonts.googleapis.com/css?family=Lato:100');
\Larakit\StaticFiles\Css::instance()
->addInline('
html, body {
height: 100%;
}
');
будут одинаковы.
###4. Работа с JS Она не намного сложнее работы с JS.
Только помимо двух методов добавляющих стили по ссылке или методом inline-вставки как в CSS, здесь есть еще два метода:
- добавление onLoad
- добавление Noscript
Чтобы продемонстрировать функционал JS-менеджера - дополним пример: сделаем так, чтобы
- после загрузки страницы вылетал алерт "привет!"
- загружалась яндекс карта
- при отключенном javascript показывался текст "Упс, включи JS!"
Напомню, все это мы делаем в файле
./app/Http/staticfiles.php
Итак, дополним его согласно новым требованиям
\Larakit\StaticFiles\Js::instance()
//подключим jQuery
->add('https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.3/jquery.min.js')
//подключим API яндекс карт
->add('//api-maps.yandex.ru/2.1/?lang=ru_RU')
->addInline('
var myMap;
// Дождёмся загрузки API и готовности DOM.
ymaps.ready(init);
function init () {
// Создание экземпляра карты и его привязка к контейнеру с
// заданным id ("map").
myMap = new ymaps.Map("map", {
// При инициализации карты обязательно нужно указать
// её центр и коэффициент масштабирования.
center: [55.76, 37.64], // Москва
zoom: 10
}, {
searchControlProvider: "yandex#search"
});
}
')
//добавим привествие при загрузке страницы
->addOnload('alert("привет!")')
//добавим сообщение при выключенном JS
->addNoscript('Упс, включи JS!');
//добавим необходиеы стили для карты
\Larakit\StaticFiles\Css::instance()
->addInline('
#map {
width: 100%;
height: 200px;
}
');
Дополним и сам шаблон:
- добавим вызов функции вставки скриптов
- добавим контейнер для карты
Обновляем страницу:
- alert при загрузке страницы вылетел
- Яндекс.карта появилась
- отключаем javascript в браузере и перезагружаем страницу - справа вверху видим надпись "Упс, включи JS!"
Смотрим как были добавлены скрипты:
###5. Способы распространения статики Их всего три:
- использование внешних ресурсов (простое подключение)
- использование статики из DOCUMENT_ROOT (простое подключение)
- использование статики из vendor-пакетов или node_modules (требует выкладки в DOCUMENT_ROOT)
Так вот для третьего пункта и существует команда, которая производит выкладку пакетов в директорию
./public/packages/...
запускается она так:
php artisan larastatic:deploy
и после ее запуска будет произведена выкладка статики для каждого зарегистрированного пакета
- заходите на github.com и вписываете в поле поиска "lk-staticfiles"
- находите пакет, например, https://github.com/larakit/sf-bootstrap
- внутри него уже есть подробнейшая документация по использованию
- подключаете его в композере
- и все!
он сам пропишется где надо и будет работать.
Понимаете? Никаких тебе танцев в написанием gulp/grunt-правил для каждого проекта. Просто прописал в composer записимость, он установился и автоматически подключился на страницу.
Причем с возможностью отключения/включения на определенных роутах по маске (см. ниже)
###6. Встроенные возможности по клиентской оптимизации
В пакете предусмотрены следующие возможности:
- минимизация CSS [inline] - по умолчанию включена только на окружении "production"
- минимизация CSS [external] - по умолчанию включена только на окружении "production"
- минимизация JS [inline] - по умолчанию включена только на окружении "production"
- минимизация JS [external] - по умолчанию включена только на окружении "production"
- минимизация JS [onload] - по умолчанию включена только на окружении "production"
- сборка всех CSS в один билд-файл [inline] - по умолчанию включена только на окружении "production"
- сборка всех CSS в один билд-файл [external] - по умолчанию включена только на окружении "production"
- сборка всех JS в один билд-файл [inline] - по умолчанию включена только на окружении "production"
- сборка всех JS в один билд-файл [external] - по умолчанию включена только на окружении "production"
- сборка всех JS в один билд-файл [onload] - по умолчанию включена только на окружении "production"
- изменение хоста, с которого раздается статика - по умолчанию пустое значение, т.е. текущий домен
А теперь рассмотрим все по отдельности: для этого добавим еще стили и скрипты, размещенные внутри проекта
- /js/js.js
- /css/css.css
<?php
\Larakit\StaticFiles\Css::instance()
->add('https://fonts.googleapis.com/css?family=Lato:100')
->add('/css/css.css')
->add('/css/css2.css')
->add('/css/css3.css')
->add('/css/css4.css')
->add('/css/css5.css')
->addInline('
html, body {
height: 100%;
}
body {
margin: 0;
padding: 0;
width: 100%;
display: table;
font-weight: 100;
font-family: "Lato";
}
.container {
text-align: center;
display: table-cell;
vertical-align: middle;
}
.content {
text-align: center;
display: inline-block;
}
.title {
font-size: 96px;
}
');
\Larakit\StaticFiles\Js::instance()
//подключим jQuery
->add('https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.3/jquery.min.js',null,true)
//подключим API яндекс карт
->add('//api-maps.yandex.ru/2.1/?lang=ru_RU',null,true)
->add('/js/js.js')
->add('/js/js2.js')
->add('/js/js3.js')
->add('/js/js4.js')
->add('/js/js5.js')
->addInline('
var myMap;
// Дождёмся загрузки API и готовности DOM.
ymaps.ready(init);
function init () {
// Создание экземпляра карты и его привязка к контейнеру с
// заданным id ("map").
myMap = new ymaps.Map("map", {
// При инициализации карты обязательно нужно указать
// её центр и коэффициент масштабирования.
center: [55.76, 37.64], // Москва
zoom: 10
}, {
searchControlProvider: "yandex#search"
});
}
')
//добавим привествие при загрузке страницы
->addOnload('alert("привет!")')
//добавим сообщение при выключенном JS
->addNoscript('Упс, включи JS!');
//добавим необходимые стили для карты
\Larakit\StaticFiles\Css::instance()
->addInline('
#map {
width: 100%;
height: 200px;
}
');
/js/js.js /js/js2.js /js/js3.js /js/js4.js /js/js5.js
confirm(
"Выполнился скрипт js.js?"
);
/css/css.css /css/css2.css /css/css3.css /css/css4.css /css/css5.css
body{
color : #EB6251;;
}
Сохраним показания YSlow перед началом оптимизации
Так выглядели подключенные стили до оптимизации
А так скрипты
###6.1 Включение сборки CSS и JS в билд-файлы Для этого в .env пропишем:
# включим сборку CSS [inline] в один файл
LARAKIT_STATIC_CSS_INLINE_BUILD=1
# включим сборку CSS [подключенные по ссылке] в один файл
LARAKIT_STATIC_CSS_EXTERNAL_BUILD=1
# включим сборку CSS [inline] в один файл
LARAKIT_STATIC_JS_INLINE_BUILD=1
# включим сборку CSS [подключенные по ссылке] в один файл
LARAKIT_STATIC_JS_EXTERNAL_BUILD=1
# включим сборку CSS [onload] в один файл
LARAKIT_STATIC_JS_ONLOAD_BUILD=1
Теперь код нашей странички стал намного компактнее:
###6.3 Исключение из билд-файла некоторых CSS и JS Мы видим, что в билды попали файлы, которые не надо собирать в билды, а значит они должны отдаваться именно из того места, откуда были подключены:
Установим для них параметр no_build в true:
- в CSS это четвертый параметр
- в JS это третий параметр
\Larakit\StaticFiles\Css::instance()
->add(
//внешний стиль
'https://fonts.googleapis.com/css?family=Lato:100',
//media - условие использования, например "all" или "print"
null,
//условие подключения, например "if IE 6"
null,
//no_build
true
)
...
;
\Larakit\StaticFiles\Js::instance()
//подключим jQuery
->add(
'https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.3/jquery.min.js',
//условие подключения, например "if IE 6"
null,
//no_build
true
)
//подключим API яндекс карт
->add(
'//api-maps.yandex.ru/2.1/?lang=ru_RU',
//условие подключения, например "if IE 6"
null,
//no_build
true
)
...
;
Результат:
###6.2 Включение минимизации CSS и JS Для этого в .env пропишем:
LARAKIT_STATIC_CSS_INLINE_MIN=1
LARAKIT_STATIC_CSS_EXTERNAL_MIN=1
Добавим в .env еще инструкций:
LARAKIT_STATIC_CSS_INLINE_MIN=1
LARAKIT_STATIC_CSS_INLINE_MIN=1
LARAKIT_STATIC_CSS_EXTERNAL_MIN=1
LARAKIT_STATIC_JS_INLINE_MIN=1
LARAKIT_STATIC_JS_EXTERNAL_MIN=1
LARAKIT_STATIC_JS_ONLOAD_MIN=1
Выложим статику еще раз,
php artisan larastatic:deploy
чтобы были перегенерированы ссылки на подключаемую статику для сброса кэша браузеров
Продемонстрируем результаты работы на примере CSS
Было
Стало
Продемонстрируем результаты работы на примере CSS
Было
Стало
###6.3 Отдача статики с другого домена
Имитируем перенос статики на CDN, для этого создадим домен "st1.staticfiles" и сделаем его алиасом для домена "staticfiles".
Затем добавим в .env еще инструкций:
LARAKIT_STATIC_PREFIX=http://st1.staticfiles
Смотрим показания YSlow перед началом оптимизации
А теперь снова запускаем YSlow для проверки результатов нашей оптимизации:
####Т.е. удалось поднять Overall performance score c 80 до 93
Смотрим показания YSlow перед началом оптимизации
А теперь снова запускаем YSlow для проверки результатов нашей оптимизации:
Результат был бы еще выше, если бы мы использовали полноценные большевесные стили и скрипты в большом количестве, а не несколько пустышек, взятых для примеров.
По-моему, не плохо для коробочного решения делающего это автоматически.
###7. Создание собственных пакетов
Ну и, пожалуй, самое главное: возможность оформлять свои пакеты. Сделаем два вида пакетов:
- с использованием CDN
- с распространением статики из пакета
Рекомендации:
- (для удобного поиска на packagist) обязательно вписывайте следующие теги "laravel, lk-staticfiles"
- (для удобного поиска на github) в Description репозитория на github вписывайте префикс "[Larakit][lk-staticfiles] "
###7.1 Создание собственных пакетов с использованием CDN
Создаем composer.json
{
"name": "larakit/sf-bootstrap",
"description": "sf-bootstrap",
"keywords": [
"larakit",
"laravel",
"laravel 5",
"bootstrap",
"lk-staticfiles"
],
"license": "MIT",
"version": "3.3.6",
"require": {
"larakit/sf-jquery": "*"
},
"autoload": {
"files": [
"init.php"
]
}
}
В разделе require укажите статические пакеты, от которых зависит ваш пакет (например, если вам нужен jQuery), а если таких нет впишите зависимость от текущего пакета
{
"require":{
"larakit/lk-staticfiles":"*"
},
}
В автоподключаемый файл init.php впишите инструкции по подключению и снова укажите зависимости от пакетов, чтобы они были подключены до создаваемого пакета
<?php
\Larakit\StaticFiles\Manager::package('larakit/sf-bootstrap')
->usePackage('larakit/sf-jquery')
->js('//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/js/bootstrap.min.js')
->css('//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.5/css/bootstrap.min.css');
Собственно все! Для использования вам достаточно прописать в композер проекта "require": "/sf-ваш_пакет"
###7.2 Создание собственных пакетов с распространением статики из пакета Точно также создаем composer.json и init.php. Единственное отличие: в init.php прописываем директорию со статикой, откуда будет производится ее выкладка в DOCUMENT_ROOT из vendor.
<?php
\Larakit\StaticFiles\Manager::package('larakit/sf-larakit-js')
//из этого относительного пути внутри пакета будет произведена выкладка
->setSourceDir('public')
//обязательно подключить указанный зависимый пакет ПЕРЕД текущим пакетом
->usePackage('larakit/sf-jquery')
//искать JS внутри директории SOurceDir
->jsPackage('js/larakit.js');
это означает, что при выкладке будет взято содержимое директории
./vendor/larakit/sf-larakit-js/public
и выложено в
./public/packages/larakit/sf-larakit-js/
Сама выкладка напомню производится путем запуска команды:
php artisan larastatic:deploy
Эта команда выполняет следующие действия:
- выкладывает в ./public/packages/ все зарегистрированные файлы из vendor пакетов
- обновляет хэш статики, чтобы сбросился кэш браузера (изменяется URL подключаемых стилей и скриптов)
###8. Правила включения/выключения пакетов В процессе работы может возникнуть такая необходимость как точечное отключение или включение используемых пакетов. Например, нам надо отключить пакет "larakit/sf-jquery" в админке. Так как мы делали все роуты админки с префиксом "admin.", то у нас есть сейчас три роута:
- admin
- admin.users
- admin.news
- admin.pages
Инструкциию для управления подключением пакетов пишем в файле ./app/Http/staticfiles.php:
####8.1 Отключение пакета jQuery только на главной странице админки
\Larakit\StaticFiles\Manager::package('larakit/sf-jquery')
->setExclude('admin');
####8.2 Отключение пакета jQuery только на внутренних страницах админки
\Larakit\StaticFiles\Manager::package('larakit/sf-jquery')
->setExclude('admin.*');
####8.3 Отключение пакета jQuery во всей админке
\Larakit\StaticFiles\Manager::package('larakit/sf-jquery')
->setExclude('admin*');
####8.4 Отключение пакета jQuery во всей админке, кроме страницы управления пользователями
\Larakit\StaticFiles\Manager::package('larakit/sf-jquery')
->setExclude('admin*')
->setInclude('admin.users');
####8.5 Отключение пакета jQuery на всем сайте, кроме страницы управления пользователями
\Larakit\StaticFiles\Manager::package('larakit/sf-jquery')
->setExclude('*')
->setInclude('admin.users');
Указанные правила можно менять местами, будет применено правило, наиболее точно описывающее текущий роут, для которого производится попытка автоподключения пакетов статики.
###... ###Profit!