Jak stworzyć środowisko developerskie i produkcyjne przy pomocy Grunt.js
Czas czytania:
Rozdzielenie projektu na wersję developerską (dev) oraz produkcyjną (dist) jest bardzo przydatnym zabiegiem. Umożliwia łatwe debugowanie błędów w wersji dev, a także porównanie rezultatów optymalizacji wersji dist. Proces ten można zautomatyzować przy pomocy Grunt.js.
Oto etapy procesu:
- Instalacja pluginów,
- Pliki źródłowe (src),
- Środowisko developerskie (dev),
- Środowisko produkcyjne (dist),
- Podsumowanie.
Poniżej przedstawiam rozwinięcie każdego z nich.
1. Instalacja pluginów
Zakładam, że Grunt.js jest przynajmniej znany w podstawowym zakresie. W przeciwnym razie polecam przeczytać wprowadzenie do Grunt.js lub artykuł w, którym został opisany proces instalacji.
Wtyczki wykorzystane w przykładzie:
- grunt-contrib-concat – łączenie wielu plików w jeden,
- grunt-contrib-copy – kopiowanie plików i folderów,
- grunt-contrib-htmlmin – minimalizacja HTML,
- grunt-contrib-uglify – minimalizacja plików JS,
- grunt-contrib-watch – śledzenie zmian w plikach,
- grunt-processhtml – przetwarzanie HTML, szablony,
Można instalować każdy z osobna lub przygotować plik package.json z listą zależności:
{
"name": "Env",
"version": "0.1.0",
"author": "Artur Bala",
"private": true,
"devDependencies": {
"grunt": "^0.4.5",
"grunt-contrib-concat": "^0.5.1",
"grunt-contrib-copy": "^0.8.0",
"grunt-contrib-htmlmin": "^0.4.0",
"grunt-contrib-uglify": "^0.9.1",
"grunt-contrib-watch": "~0.6.1",
"grunt-newer": "^1.1.0",
"grunt-processhtml": "^0.3.7"
}
}
i uruchomić komendę:
npm install
2. Pliki źródłowe (src)
Wyjściowa struktura plików:
root
|-- Gruntfile.js
|-- node_modules
| |-- [...]
|-- package.json
|-- src
| |-- js
| | |-- libs
| | | |-- lib-1.js
| | | |-- lib-2.js
| | |-- scripts.js
| |-- template
| | |-- includes
| | | |-- footer.html
| | | |-- header.html
| | |-- index.html
W katalogu z plikami źródłowymi src w powyższym przykładzie umieszczone są pliki .js oraz pliki szablonów używane przez grunt-processhtml.
W tym przykładzie w header.html nie ma nic nadzwyczajnego:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Test Env</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
</head>
<body>
<!--[if lte IE 8]>
<p class="browsehappy">You are using an <strong>outdated</strong>
browser. Please
<a href="http://browsehappy.com/">upgrade your browser</a> to
improve your experience.</p>
<![endif]-->
<div id="container" class="container">
<div class="container-inner">
<!-- Header -->
<header id="header" class="header" role="banner">
Header
</header>
<!-- end Header -->
W pliku index.html załączany jest header i footer:
<!-- build:include header.html --><!-- /build -->
<!-- Content -->
<main id="content" class="content" role="main">
<div class="wrap">
Content
</div>
</main>
<!-- end Content -->
<!-- build:include footer.html --><!-- /build -->
Natomiast w footer.html w zależności od środowiska, załączone są wszystkie pliki .js lub jeden skompresowany:
</div>
<!-- Footer -->
<footer id="footer" role="contentinfo">
Footer
</footer>
<!-- end Footer -->
</div>
<!-- Scripts -->
<!-- build:template
<% if (environment === 'dev') { %>
<script src="assets/js/libs/lib-1.js"></script>
<script src="assets/js/libs/lib-2.js"></script>
<script src="assets/js/scripts.js"></script>
<% } else { %>
<script src="assets/js/scripts.min.js" defer></script>
<% } %>
/build -->
<!-- end Scripts -->
</body>
</html>
Cała konfiguracja znajduje się w pliku Gruntfile.js:
module.exports = function(grunt){
'use strict';
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
// Processhtml
processhtml: {
options: {
process: true,
recursive: true,
includeBase: 'src/template/includes/'
},
dev: {
options: {
environment: 'dev'
},
files: [
{
expand: true,
cwd: 'src/template/',
src: ['*.html'],
dest: 'dev/',
ext: '.html'
}
]
},
dist: {
options: {
environment: 'dist'
},
files: [
{
expand: true,
cwd: 'src/template/',
src: ['*.html'],
dest: 'dist/',
ext: '.html'
}
]
}
},
// Copy
copy: {
dev: {
files: [
{
expand: true,
cwd: 'src/js/',
src: '**',
dest: 'dev/assets/js/',
filter: 'isFile'
}
]
}
},
// HTML min
htmlmin: {
dist: {
options: {
removeComments: true,
collapseWhitespace: true,
minifyJS: true
},
files: {
'dist/index.html': 'dist/index.html'
}
}
},
// Concat
concat: {
dist: {
src: [
'src/js/libs/lib-1.js',
'src/js/libs/lib-2.js',
'src/js/scripts.js'
],
dest: 'dist/assets/js/scripts.js',
}
},
// Uglify
uglify: {
options: {
preserveComments: 'some'
},
dist: {
files: {
'dist/assets/js/scripts.min.js': ['dist/assets/js/scripts.js']
}
}
},
// Watch
watch: {
js: {
files: ['src/js/**/*.js'],
tasks: ['copy:dev']
},
html: {
files: ['src/template/**/*.html'],
tasks: ['processhtml:dev']
}
}
});
grunt.loadNpmTasks('grunt-contrib-htmlmin');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-processhtml');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('dev', ['processhtml:dev', 'copy:dev', 'watch']);
grunt.registerTask('dist', ['processhtml:dist', 'htmlmin', 'concat:dist', 'uglify:dist']);
grunt.registerTask('default', ['dev']);
};
3. Środowisko developerskie (dev)
Uruchomienie komendy:
grunt dev
lub z powodu tego, że „dev” jest domyślnym taskiem, również komendy:
grunt
spowoduje uruchomienie następujących procesów:
grunt.registerTask('dev', ['processhtml:dev', 'copy:dev', 'watch']);
– processhtml:dev
dev: {
options: {
environment: 'dev'
},
files: [
{
expand: true,
cwd: 'src/template/',
src: ['*.html'],
dest: 'dev/',
ext: '.html'
}
]
}
Odpowiada za przetworzenie szablonów HTML z katalogu templates i umieszczenie wynikowego pliku index.html w katalogu dev. Dzięki ustawieniu parametru environment na 'dev’ i warunkowi w footer.html:
<!-- build:template
<% if (environment === 'dev') { %>
<script src="assets/js/libs/lib-1.js"></script>
<script src="assets/js/libs/lib-2.js"></script>
<script src="assets/js/scripts.js"></script>
<% } else { %>
<script src="assets/js/scripts.min.js" defer></script>
<% } %>
/build -->
ładowane są wszystkie 3 pliki.js w niepołączonej, nieskompresowanej postaci.
– copy:dev
Kopiuje pliki .js z src do dev.
– watch
Nasłuchuje zmian w plikach .js oraz plikach szablonów i uruchamia odpowiednio proces processhtml:dev lub copy:dev.
Wynikowa struktura plików:
root
|-- dev
| |-- assets
| | |-- js
| | | |-- libs
| | | | |-- lib-1.js
| | | | |-- lib-2.js
| | | |-- scripts.js
| |-- index.html
|-- Gruntfile.js
|-- node_modules
| |-- [...]
|-- package.json
|-- src
| |-- [..]
oraz podgląd z konsoli Chrome dla strony w wersji developerskiej:
4. Środowisko produkcyjne (dist)
Utworzenie wersji produkcyjnej realizowane jest przy pomocy komendy:
grunt dist
Uruchamiane są procesy:
grunt.registerTask('dist', ['processhtml:dist', 'htmlmin', 'concat:dist', 'uglify:dist']);
Analogicznie do wersji dev, pliki szablonów są przetwarzane w procesie processhtml:dist, z tym że wynikowa postać trafia do katalogu dist. W stopce ładowany jest tylko jeden plik scripts.min.js , zminimalizowany i złączony za pomocą procesów concat:dist oraz uglify:dist. Dodatkowo HTML zostaje zminimalizowany przez htmlmin.
Wynikowa struktura plików:
root
|-- dev
| |-- [...]
|-- dist
| |-- assets
| | |-- js
| | | |-- scripts.js
| | | |-- scripts.min.js
| |-- index.html
|-- Gruntfile.js
|-- node_modules
| |-- [...]
|-- package.json
|-- src
| |-- [..]
oraz podgląd z konsoli Chrome dla wersji produkcyjnej:
Podsumowanie
Wersja developerska projektu umożliwia w łatwy sposób debugowanie błędów, co w przypadku wersji produkcyjnej może być utrudnione przez zabiegi optymalizacyjne (takie jak np. minimalizacja JS, minimalizacja HTML, cache itp.). Mając dwie wersje można porównać także, w jakim stopniu optymalizacja przyniosła pomyślne efekty. W celu uproszczenia, przedstawiony przykład przedstawił wyłącznie dwa sposoby optymalizacji, bez uwzględnienia plików CSS, obrazków, cache itd. Jest to temat na osobny artykuł. 🙂
Zainteresował Cię ten artykuł?
Może Cię również zainteresować:
5 rzeczy, na które warto zwrócić uwagę, wybierając dedykowany system klasy ERP, WMS lub LMS
Tworzenie dedykowanych aplikacji web’owych (dostępnych przez przeglądarkę WWW z poziomu komputera, tabletu czy telefonu) jest… Read More
Warsztaty Discovery – 5 powodów dla których warto je przeprowadzić
Post pochodzi bezpośrednio z naszych oficjalnych kanałów na Social Media. W dynamicznym… Read More
Optymalizacja eCommerce vs. Zewnętrzny Dyrektor Technologiczny
🛠️ Studium przypadku 🛠️Post pochodzi bezpośrednio z naszych oficjalnych kanałów na Social… Read More