Když jsem vytvářel pracovní workflow pro různorodé projekty a vždy jsem chtěl mít stejný základ, ze kterého budu vycházet (to mimochodem všem doporučuji a vždycky mě překvapí, když se tak v některých i velkých agenturách neděje), netušil jsem, že zdrojové soubory pro CSS se rozrostou natolik, že kompilování bude tak pomalé, že vás práce nebude bavit.
Sám vždycky říkám, že nedělám weby, kde kompilování CSS zabere více jak 500 ms. (Bohužel dokončuji jeden projekt, kde jsem workflow nevymýšlel, kompilace trvá na mém již sice starším, ale pořád výkoném iMacu, více jak 8 vteřin. Jedním slovem - strašné. A pokud se jako já dozvíte, že to zrychlit nejde, odkažte takové experty na můj článek, předem děkuji).
Jaký zvolit preprocesor?
Na preprocesoru příliš nezáleží, ale... Sám jsem si vyzkoušel všechny tři známé, dlouhou dobu jsem používal a všem doporučoval Less díky jeho jednoduchosti, snadné instalaci a lehkosti naučení pro ještě tolik nezkušené frontendisty. Pokud jste ale i programátoři a umíte tak i uvažovat, můžete mít pocit, že vám pořád něco chybí. Zalíbil se mi i Stylus, jeho problém není ani tak jeho samotným problémem, zkrátka nemá takovou podporu a v editorech typu Sublime přece psát nebudu :-) (v jeskyních už taky dávno nežijeme, tak se nebraňme kvalitnímu IDE jako je PhpStorm, WebStorm, NetBeans apod. a nevracejme se k poznámkovému bloku). A nakonec jsem se vrátil k Sass, který se mi líbil i dříve, má větší podporu v PhpStormu, jednoduchou a jednotnou sice starší syntaxi Sass (která doufám nebude v nejbližší době deprecated :-) ), která se mi celkem zamlouvá. Rychle si zvyknete vynechávat všechny ty nepotřebné závorky, v týmu budete psát všichni jednotně.
Tento benchmark ukazuje, že Sass je ve spojení s Libsass zřejmě to nejlepší, co můžete z preprocesorů používat.
Co je špatně na všech ukázkových Gulp a Grunt úkolech?
Když narazím na návody ke kompilování CSS, většinou mají takovou základní myšlenku:
gulp.task("styles", function() {
return gulp.src("main.sass")
.pipe(sass())
.pipe(gulp.dest("built"))
});
gulp.watch("**/*.scss", ["styles"]);
Pokud se sami zamyslíte, jak tento kód funguje, sami zjistíte, že nebude příliš optimální. V souboru main.sass, který se kompiluje, je totiž spousta časem přibývajících importů, které načítají další a další soubory. Při každé změně kteréhokoli zdrojového souboru se tak musí všechny soubory zkompilovat a to zabere čas, někdy i více času než byste chtěli. A pokud rádi používáte BrowserSync, tak vás to velmi brzo přestane bavit. Není nic horšího, než čekat několik vteřin, než se vám promítne v prohlížeči změna, pokud chcete změnit barvu, velikost písma nebo jen odstraníte překlep.
Myšlenka pro rychlejší kompilování
Abychom docílili co nejrychejšího času kompilování Sass, Less nebo Stylus souborů do CSS, musíme kompilovat jen ty soubory, které aktuálně měníme. Proč bychom měli kompilovat i soubory, které nesouvisí s našimi aktuálními změnami? Pokud si chcete uklidit stůl, taky nezačnete uklízet všechny stoly v okolí, i když by si to třeba některé z nich zasloužily :-)
Jak to bude fungovat:
- Veškerá potřebná konfigurace bude v samostatném souboru (případně ve více souborech), například config.sass.
- Před spuštěním sledování změn ve zdrojových souborech se jednou provede kompilace všech souborů do CSS tak, že před zdrojový soubor nalepíme konfigurační soubor a zkompilujeme do CSS.
- Všechny zkompilované soubory spojíme do jednoho výsledného CSS souboru.
- Při jakékoli další změně zkompilujeme aktuální zdrojový soubor do CSS (stejným způsobem jako v bodu 2) a opět spojíme všechny CSS soubory.
- Je potřeba psát kód nezávisle na pořadí ve výsledném souboru.
- Na extendování doporučuji používat placeholdery (pro které v době psaní článku není v Less podpora) a mít je importované v potřebných souborech. Kromě toho bude hned vidět, na čem je daný soubor závislý.
- Pravděpodobně přijdete o sourcemapy, experimentoval jsem s jejich spojováním, ale nikam to nevedlo (a samozřejmě to pak opět kompilaci zpomalilo). Pokud byste někdo věděl, jak je zfunkčnit, budu rád, když napíšete do komentáře.
Pokud máme v projektu 300 souborů a budeme jeden upravovat, pak se bude provádět kompilace jen 1 souboru a výsledek spojíme se zbývajícími 299 soubory. Tímto způsobem rapidně zrychlíme celý proces kompilace CSS.
Trochu čísel a jak se mi desetkrát zrychlila kompilace (z 800 ms na 80 ms)
Na mém současném projektu jsem používal Less a po každém uložení souboru trvala kompilace kolem 800 ms. Pouhým přechodem na Sass (kompilace přes Libsass) kompilování trvalo okolo 400 ms. Aplikováním tohoto způsobu jsem docílil, že kompilování trvá pouhých 80 ms a kdykoli soubor některý uložím, okamžitě a bez viditelné prodlevy vidím v prohlížeči, co se děje. A s tím je radost pracovat! :-)
Implementace pomocí Gulpu
Spustit můžete pomocí gulp watch, proběhne zkompilování všech souborů a uloží se do temp adresáře. Nastaví se sledování zdrojových souborů a při změně jednoho souboru se provede jeho kompilace, tento zkompilovaný soubor se pak spojí se všemi původními a může se například přes BrowserSync přenačíst.
var gulp = require("gulp");
var $ = require("gulp-load-plugins")();
var path = require("path");
var glob = require("glob");
var fs = require("fs");
/**
* Kompiluje zdrojový soubor do CSS
* @param {String} filePath
* @param {Function} [callback]
*/
function compileFile(filePath, callback)
{
var target = filePath.replace("/styles/", "/temp/"),
sassOptions = {
includePaths: [
"www/styles/",
path.dirname(filePath)
],
errLogToConsole: true
};
var stream = gulp.src([
"www/styles/config.sass",
filePath
])
.pipe($.concat(path.basename(target)))
.pipe(gulp.dest(path.dirname(target)))
.pipe($.sass(sassOptions))
.on("error", $.notify.onError(function (error) {
return "Compile error: \n" + error.message;
}))
.on("end", function() {
if (typeof callback === "function") {
callback();
}
})
.pipe(gulp.dest(path.dirname(target)));
}
/*
* Úkol na kompilaci všech souborů do CSS,
* spouští se 1x před spuštěním sledování
*/
gulp.task("compile-sass-files", function () {
glob.sync("www/styles/**/*.sass").forEach(function (filePath) {
compileFile(filePath);
});
makeFinalCss();
});
/**
* Vytváří finální CSS soubor
* @param {Function} [callback]
*/
function makeFinalCss(callback)
{
var stream = gulp.src("www/temp/**/*.css")
.pipe($.concat("main.css"))
.pipe(gulp.dest("www/built"))
.on("end", function () {
if (typeof callback === "function") {
callback();
}
});
// CSS inject přes BrowserSync
// if (browserSync) {
// stream.pipe(browserSync.reload({
// stream: true
// }));
//}
}
/**
* Úkol na sledování zdrojových Sass souborů
**/
gulp.task("watch", ["compile-sass-files"], function ()
{
gulp.watch("www/styles/**/*.sass")
.on("change", function(file) {
console.log("Kompiluji soubor", file.path);
console.time("Kompilace");
compileFile(file.path, function() {
makeFinalCss(function() {
console.timeEnd("Kompilace");
});
});
});
});