Tento článek patří do seriálu Zrychlení webu a dev stack. Ostatní články seriálu:
- Cachujeme, GZipujeme, zrychlujeme
- SVG a PNG Sprite, zrychlujeme podruhé
- Minifikace - zrychlujeme po třetí
- DataURI - zrychlujeme po čtvrté
- Webpack lusknutím prstu - Laravel Mix - Alternativa ke Gruntu a Gulpu
- Fiddler - web debugger
- Nastavení GZip komprese u souborů s fonty
- Automatizace s Gruntem - Automatizace ostatních technik ze seriálu
- Systém SVG ikon s Gulpem
Grunt i všechny jeho balíčky jsou napsané v NodeJS, takže je asi jasné, že si jej musíme nainstalovat také. Naštěstí je stejně jako většina moderních jazyků multiplatformní, a funguje pod Windows i Linuxem. Když máme NodeJS, je zapotřebí Node package manager, zkráceně NPM (I když možná ne...). Ten někdy je součástí instalace NodeJS. Poté si můžeme hledat pluginy, a samozřejmě nainstalujeme Grunt.
Grunt lze velmi výhodně využít k automatizaci technik pro zrychlení načítání, o kterých jsem psal v předchozích článcích.
package.json
Pokud si najdeme balíček, většinou obsahuje příkaz k instalaci podobný tomu níže. Přepínač --save-dev totiž vloží informace o aktuálním balíčku do souboru package.json, díky čemuž do GITu nemusíme nahrávat všechny balíčky, které mohou mít stovky MB.
# Nainstaluje balíček grunt-contrib-clean a vloží do package.json npm install grunt-contrib-clean --save-dev # Nainstaluje všechny balíčky zmíněné v package.json npm install
Stačí na jiném počítači spustit npm install a všechny balíčky se nainstalují také. Soubor package.json můžete vytvořit ručně a vložit základní kostru, jako je ukázáno níže. Nebo stačí zadat npm init, který soubor vytvoří za vás. Někdy stačí pouze vložit { }
{
"name": "blog",
"devDependencies": { }
}
Balíčky a jejich nastavení
Další soubor, který vytvoříme se jmenuje Gruntfile.js a vložíme jej do hlavní složky projektu. Výhodou konfiguračního souboru je, že je nečekaně napsán v JavaScriptu, takže si v něm můžeme libovolně volat funkce a psát skripty. Celý můj Gruntfile najdete níže a obsahuje plno balíčků pro kompilaci React JSX syntaxe do JavaScriptu, Sassu do CSS, spojení CSS a JS souborů, build a spuštění serveru v Golangu a také automatickou synchronizaci prohlížeče. To vše automaticky po změně souborů. Gruntem se ale také dají automatizovat testy a mnohé další.
Soubor není krátký, ale zato jsem komentáře vložil přímo do něj. Celý soubor můžete také stáhnout, pokud se lépe bude číst v editoru.
module.exports = function(grunt) {
// Vlastní funkce, kterou si zavoláme níže v tasku concat
this.getConcatArray = function(dist) {
// POZOR
// Minifikovaný React je produkční verze, neminifikovaná je vývojová verze
// Vývojová verze obsahuje dodatečné funkce pro kontrolu, které zpomalují běh a v provozu by neměly být.
var useMinfiedPlugins = false;
var concatFiles = [
// concat spojuje soubory v abecedním pořadí
// Zde si prioritizuji nějaké soubory, a určím pro ně pevné pořadí
'<%= wwwAssets %>/js/src/plugins/**/*.js',
// Prvně vložíme všechny zkomilované JSX soubory až poté pluginy a další
'<%= wwwAssets %>/js/src/_*._jsx.js', // soubory začínající _ mají přednost
'<%= wwwAssets %>/js/src/stores/*._jsx.js', // Poté ze složky store
'<%= wwwAssets %>/js/src/components/*._jsx.js', // Dále ze složky components
'<%= wwwAssets %>/js/src/*._jsx.js', // Až poté všechny ostatní ve složce src končící na ._jsx.js
'<%= wwwAssets %>/js/src/main._jsx.js', // Hlavní JSX soubor až poslední
'<%= wwwAssets %>/js/src/*.js', // Pak všechny ostatní JS soubory, již vložené dříve se znova nevloží
];
if (useMinfiedPlugins || dist == true) {
// Unshift vloží na začátek pole. React musí být vždy první
// Druhý řádek, negace značí, že soubor react.js se vložit nesmí, protože se vkládá react.min.js
concatFiles.unshift('<%= wwwAssets %>/js/src/plugins/react.min.js');
concatFiles.push('!<%= wwwAssets %>/js/src/plugins/react.js');
}else{
concatFiles.unshift('<%= wwwAssets %>/js/src/plugins/react.js');
concatFiles.push('!<%= wwwAssets %>/js/src/plugins/react.min.js');
}
return concatFiles;
}
// Samotná konfigurace Gruntu
grunt.initConfig({
wwwAssets: 'www', // Pouze proměnná, je použita mnohokrát níže
// Task compass převede SASS s frameworkem Compass do CSS
// Celý task má 2 cíle, dist a debug které můžou být spuštěny podle potřeby
// Nastavení balíčků najdete vždy u každého z nich v dokumentaci
compass: {
dist: {
options: {
config: 'config.rb',
sourcemap: true,
force: true
}
},
debug: {
options: {
config: 'config.rb',
sourcemap: false,
outputStyle: 'expanded'
}
}
},
// PostCSS je CSS postprocessor, obsahuje množství pluginů
// Já zde použil pouze Autoprefixer
postcss: {
options: {
// map: true, // inline sourcemaps
processors: [
require('autoprefixer')({browsers: '>1%'}), // add vendor prefixes
]
},
dist: {
src: '<%= wwwAssets %>/css/styles.css',
dest: '<%= wwwAssets %>/css/styles.css'
}
},
// Balíček pro sledování změn v souborech
// pokud se specifikovaný soubor změní, spustí jednotlivé tasky
watch: {
sass: {
files: '<%= wwwAssets %>/css/src/*.scss',
tasks: ['compass:debug', 'postcss']
},
react: {
// Jakýkoli soubor co má příponu JSX a nachází se ve složce www/js/jsx/ nebo libovolné podsložce
files: '<%= wwwAssets %>/js/jsx/**/*.jsx',
// Spustí task clean s cilem react, babel a target debug v tasku concat
tasks: ['clean:react', 'babel', 'concat:debug']
},
js: {
// Jakýkoli *.js soubor v www/js/src/, ale nesmi končit na *._jsx.js
// Tento soubor může být změnen v targetu react a provedl by se zbytečně 2x
files: ['<%= wwwAssets %>/js/src/*.js', '!<%= wwwAssets %>/js/src/*._jsx.js'],
tasks: ['concat:debug']
}
},
// Vymaže všechny soubory končící na ._jsx.js ve složce www/js/src/ a všech podsložkách
clean: {
react: ['<%= wwwAssets %>/js/src/**/*._jsx.js']
},
// Provede spojení daných souborů v 1 jediný
concat: {
options: {
separator: '\n',
process: function(src, filepath) {
return '\n// Source: ' + filepath + '\n' + src;
},
},
// target debug a dist, oba volají funkci definovanou nahoře která vrátí pole souborů, které spojit a v jakém pořadí
debug: {
src: this.getConcatArray(false),
dest: '<%= wwwAssets %>/js/scripts.js'
},
dist: {
src: this.getConcatArray(true),
dest: '<%= wwwAssets %>/js/scripts.js'
}
},
// Kompilace JSX syntaxe a ES2015 do běžného JavaScriptu
babel: {
// https://php.quicoto.com/use-babel-to-compile-react-jsx-with-grunt/
options: {
presets: ['stage-0', 'es2015', 'react']
},
jsx: {
files: [{
// Zde se soubory ve složce www/js/jsx/ a podsložek přeloží a
// uloží do složky www/js/src/ ale složky zůstanou zachovány
// Příklad: www/js/jsx/main.jsx -> www/js/src/main._jsx.js
// Nebo: www/js/jsx/stores/articles.jsx -> www/js/src/stores/articles._jsx.js
expand: true,
cwd: '<%= wwwAssets %>/js/jsx/',
src: ['**/*.jsx'],
dest: '<%= wwwAssets %>/js/src/',
ext: '._jsx.js'
}]
}
},
// Task který kontroluje soubory a pokud se změní, provede refresh prohlížeče
browserSync: {
dev: {
// Které soubory sledovat, chci pouze výsledné, které se uloží až
// Compass zkompiluje SASS soubory do finálního CSS
// Babel a Concat přeloží a spojí všechny JS soubory
bsFiles: {
src : [
'<%= wwwAssets %>/css/styles.css',
'<%= wwwAssets %>/js/scripts.js'
]
},
options: {
// Můj server běží na této adrese
// browserSync dokáže i server vytvořit a servírovat HTML soubory
proxy: 'https://localhost:1443',
watchTask: true
}
}
},
// Run slouží ke spuštění příkazů jako v příkazové řádce
// Server se kompiluje a spustí pomocí Makefile a task run spustí tento soubor
// Až se v příkazové řádce objeví "./blog" tak se tváří jako vyřízený a pokračují další tasky
run: {
options: {
ready: new RegExp("\./blog"),
wait: false,
},
GoServer: {
cmd: 'make',
args: [
'run'
]
}
}
});
// Musíme načíst jednotlivé balíčky, které chceme používat
grunt.loadNpmTasks('grunt-contrib-compass');
grunt.loadNpmTasks('grunt-babel');
grunt.loadNpmTasks('grunt-postcss');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-browser-sync');
grunt.loadNpmTasks('grunt-run');
// Registrujeme vlastní tasky a specifikujeme, co daný task zastupuje
// Můžete si všimnout, že tasky se dají skládat, default task spustí také můj task build-debug
// Každý task se spustí jako task:target, přičem záleží na balíčku co udělají, pokud target není specifikovaný
// Některý spustí pouze 1, některé jako watch všechny
grunt.registerTask('default', ['build-debug', 'run', 'browserSync', 'watch']);
grunt.registerTask('build', ['clean', 'compass:dist', 'postcss', 'babel', 'concat:dist']);
grunt.registerTask('build-debug', ['clean', 'compass:debug', 'postcss', 'babel', 'concat:debug']);
};
Nyní můžeme pomocí příkazů spouštět jednotlivé tasky ať už vlastní, nebo definované. Pokud však žádný neuvedeme, spustí se default. V případě Gruntu je potřeba se přepnout pomocí příkazu cd do složky daného projektu.
# Spustí default task grunt # Spustí build-debug, což zahrnuje clean, compass:debug, postcss... grunt build-debug # Spustí pouze task clean grunt clean
K tomuto článku již není možné přidávat další komentáře
Komentáře
Zdravím a děkuji za dobře napsaný článek. S gruntem se teprve seznamuji a každá ukázka použití se hodí :)