Die Idee, Ihren Code in kleinere, mundgerechte Stücke zu zerlegen, schafft eine Umgebung, die einfach zu bearbeiten und zu pflegen ist. Das wird oft als Modul-Design betrachtet und ist heutzutage ein Standard für die Webentwicklung. Ich zeige Ihnen eine Möglichkeit, wie Sie Moduldesign nutzen können, um Ihre Grunt-Aufgaben besser zu organisieren.
Ich gehe davon aus, dass Sie die Grundlagen der Grunt-Nutzung bereits kennen. Wenn nicht, hier ist ein Artikel von Chris, der Ihnen den Einstieg erleichtert: Grunt für Leute, die Dinge wie Grunt seltsam und schwierig finden.
Wenn Sie Grunt verwenden, sind Sie wahrscheinlich daran gewöhnt, dass Ihre Gruntfile wie folgt aussieht.
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
sass: {
dist: {
options: {
style: 'expanded',
sourcemap: 'none'
},
files: {
'style.css': 'sass/global.scss',
'css/dev.style.css': 'sass/global.scss',
'css/ie9.style.css': 'sass/ie9.scss',
}
}
},
postcss: {
options: {
processors: [
require('autoprefixer')(),
require('rucksack-css')({ fallbacks: true })
]
},
dist: {
src: 'style.css',
dest: 'style.css'
},
dev: {
src: 'css/dev.style.css',
dest: 'css/dev.style.css'
},
},
cssmin: {
target: {
files: {
'style.css': 'style.css'
}
}
},
concat: {
dist: {
src: [
'js/lib/no-conflict.js',
'js/lib/skip-navigation.js',
],
dest: 'js/scripts.js'
},
},
jshint: {
files: [
'js/scripts.js',
'js/ie.js',
],
options: {
scripturl: true,
globals: {
jQuery: true
}
}
},
uglify: {
options: {
mangle: false,
compress: true,
quoteStyle: 3
},
dist: {
files: {
'js/head.min.js': 'js/head.js',
'js/scripts.min.js': 'js/scripts.js',
'js/ie.min.js' : 'js/ie.js',
}
}
},
watch: {
scripts: {
files: ['js/**/*.js'],
tasks: ['concat', 'uglify'],
options: {
spawn: false
}
},
css: {
files: ['sass/**/*.scss'],
tasks: ['sass', 'postcss', 'cssmin']
}
},
});
grunt.loadNpmTasks('grunt-postcss');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-cssmin');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-jsvalidate');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.registerTask('default', ['watch']);
};
Das ist eine ziemlich normal aussehende Gruntfile. Eigentlich ist das eine ziemlich kleine. Ich habe Gruntfiles gesehen, die dreimal so groß waren. Wenn ich mir diese großen Gruntfiles ansehe, bekomme ich Kopfschmerzen. Das Moduldesign wurde mir so sehr eingeprägt, dass mich eine so große Gruntfile aus dem Konzept bringt. Warum kann meine Gruntfile nicht mehr so aussehen?
module.exports = function(grunt) {
var tasks = {scope: ['devDependencies', 'dependencies' ]};
var options = {config: { src: "grunt/*.js" }};
var configs = require('load-grunt-configs')(grunt, options);
require('load-grunt-tasks')(grunt, tasks);
grunt.initConfig(configs);
grunt.registerTask('default', ['watch']);
};
Das kann sie! Ich zeige Ihnen, wie.
Um dies zu erreichen, werden wir zwei Grunt-Pakete installieren.
Installieren Sie diese Pakete wie jedes andere Grunt-Paket.
$ npm install --save-dev load-grunt-tasks
$ npm install --save-dev load-grunt-configs
Nun sollte Ihre `package.json`-Datei die beiden Pakete wie folgt enthalten.
{
"name": "your-project",
"version": "1.0.0",
"description": "",
"main": "Gruntfile.js",
"scripts": {
"test": "echo "Error: no test specified" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"grunt-contrib-cssmin": "^1.0.1",
"load-grunt-configs": "^1.0.0",
"load-grunt-tasks": "^3.5.0"
}
}
Zu diesem Artikel habe ich auch grunt-contrib-cssmin aufgenommen. Ihre `package.json`-Datei sollte alle Grunt-Pakete enthalten, die Sie für Ihr Projekt benötigen.
Lassen Sie uns nun mit einer neuen `Gruntfile.js` beginnen. Erstellen Sie eine neue `Gruntfile.js`-Datei und fügen Sie Folgendes hinzu.
module.exports = function(grunt) {
}
Als Erstes richten wir load-grunt-tasks ein. Was load-grunt-tasks tut, ist, dass es alle Ihre `grunt.loadNpmTasks()` für Sie erstellt. Dies erspart Ihnen das mühsame Schreiben jeder einzelnen `grunt.loadNpmTasks()` von Hand.
Erstellen wir eine Variable `tasks`, die die Optionen für das load-grunt-tasks-Paket definiert. Wir definieren lediglich den Geltungsbereich und weisen es an, eine `grunt.loadNpmTasks()` für alle `devDependencies` und `dependencies`-Pakete zu erstellen, die in der `package.json`-Datei definiert sind.
module.exports = function(grunt) {
var tasks = {scope: ['devDependencies', 'dependencies']};
}
Wir müssen es `require()` und die Variable `tasks` als zweites Argument hinzufügen. Fügen wir auch zwei weitere Variablen **options** und **configs** für load-grunt-configs hinzu.
module.exports = function(grunt) {
var tasks = {scope: ['devDependencies', 'dependencies' ]};
var options = {config: { src: "grunt/*.js" }};
var configs = require('load-grunt-configs')(grunt, options);
require('load-grunt-tasks')(grunt, tasks);
}
In der **options**-Variable teilen wir load-grunt-configs mit, wo es nach den Dateien suchen soll, die die Grunt-Aufgabenoptionen enthalten. Die Variable **configs** ist einfach das Einbinden von load-grunt-configs und das Hinzufügen von **options** als zweites Argument.
Schließlich fügen wir die Variable `configs` in die Funktion `grunt.initConfig()` ein und registrieren eine einfache Aufgabe, die grunt-contrib-cssmin ausführt.
module.exports = function(grunt) {
var tasks = {scope: ['devDependencies', 'dependencies' ]};
var options = {config: { src: "grunt/*.js" }};
var configs = require('load-grunt-configs')(grunt, options);
require('load-grunt-tasks')(grunt, tasks);
grunt.initConfig(configs);
grunt.registerTask('default', ['cssmin']);
}
Das ist alles, was wir in die Gruntfile aufnehmen werden. Die einzigen weiteren Dinge, die Sie hinzufügen möchten, wären zusätzliche Aufgaben, die Sie registrieren möchten. Wie eine **watch**-Aufgabe oder eine **build**-Aufgabe.
In der Variablen `options` haben wir den *src* als `grunt/*.js` definiert. Dies weist load-grunt-configs an, in einem Verzeichnis namens **grunt** zu suchen und alle darin enthaltenen JavaScript-Dateien einzuschließen.
Lassen Sie uns ein Verzeichnis namens **grunt** erstellen und die Aufgabenoptionen für cssmin hinzufügen, indem wir eine Datei namens `cssmin.js` erstellen. Ihre Projektverzeichnisstruktur sollte jetzt ähnlich aussehen.

Fügen wir die cssmin-Optionen zur Datei `cssmin.js` hinzu. Das Erste, was Sie hinzufügen möchten, ist die Grunt-Variable `module.exports`, genau wie in der Gruntfile, damit Grunt weiß, dass diese Datei beim Ausführen Ihrer Grunt-Aufgabe geladen werden soll. Fügen Sie dann die Optionen für cssmin hinzu.
module.exports = {
target: {
files: {
'style.css': 'styles.css'
}
}
};
Wenn Sie bemerkt haben, habe ich die `cssmin: {}`-Klammern nicht hinzugefügt, wie Sie es in einer einfachen Gruntfile tun würden. Das liegt daran, dass load-grunt-configs den Dateinamen verwendet, um zu erkennen, welche Aufgabe ausgeführt wird. Wenn Sie zum Beispiel grunt-contrib-uglify verwenden, wäre der Dateiname `uglify.js`. Wenn Sie grunt-postcss verwenden, wäre der Dateiname `postcss.js`.
So fügen wir das Moduldesignkonzept zu unseren Grunt-Aufgaben hinzu. Jede Aufgabe erhält eine eigene Datei, die die Optionen der Aufgabe enthält. Das erleichtert das Hinzufügen neuer Aufgaben und ermöglicht es mehreren Entwicklern, Änderungen vorzunehmen, ohne sich Sorgen machen zu müssen, versehentlich eine andere Aufgabe in einer riesigen Gruntfile durcheinanderzubringen.
Wenn Sie nun Ihre Grunt-Aufgabe ausführen. Grunt sucht im Ordner `grunt`, findet alle Aufgabendateien und führt diese Aufgaben mit den Optionen aus, die Sie in jeder Datei definiert haben.
Dies war ein einfaches Beispiel für diese Technik. Die Pakete load-grunt-tasks und load-grunt-configs bieten weitere Optionen, die Ihnen noch mehr Kontrolle über Ihre Grunt-Aufgaben geben können.
Ich hoffe, das hilft Ihnen, Ihre Gruntfile in den Griff zu bekommen und Ihren Projekten mit Grunt mehr Flexibilität zu verleihen.
Benutzt noch jemand Grunt in nicht veralteten Projekten?
Ja. https://github.com/LotusTM/Kotsu/tree/release/1.0.0
Ich benutze es immer noch — ich hatte noch keinen wirklich zwingenden Grund, auf das zu wechseln, was gerade angesagt ist (ist es webpack? NPM-Skripte? Hassen wir jetzt Gulp? Kann das nicht mehr verfolgen).
Ich bin sicher, es gibt viele Gründe, zu etwas anderem zu wechseln, aber Grunt erledigt das Kompilieren von SCSS/Minifizieren von Dingen/etc. gut. Was sind Ihre Hauptkritikpunkte an Grunt? Ist es, dass es zu viel Boilerplate gibt oder gibt es etwas Falsches daran, das sich tatsächlich auf Endbenutzer auswirkt?
Ich benutze es auch noch. Ich habe noch keinen zwingenden Grund gefunden, den Workflow in meinem Team zu ändern. Ich halte aber immer die Augen offen.
Relevant
@Alex – Der Grund, warum ich persönlich von Grunt zu Gulp gewechselt bin, war, dass ich das Gefühl hatte, Gulp sei beim Ausführen von Aufgaben deutlich schneller. Es könnte auch gut sein, dass meine Gruntfile schlecht geschrieben war – ich habe keine wirklichen, harten Tests damit durchgeführt.
Hmm, das ist fair. Das einzige signifikante Performance-Problem, das ich hatte, war das Kompilieren von SCSS. Für mich hat der Wechsel von `grunt-contrib-sass` (das Ruby Sass verwendet) zu `grunt-sass` (das LibSass verwendet) viel geholfen.
Wenn ich merke, dass die Dinge zu langsam werden, nehme ich das vielleicht als Anlass, zu etwas anderem zu wechseln, danke.
Ich habe auch Ruby Sass verwendet, und als ich wechselte, bin ich auch zu PostCSS gewechselt, beides hat sicherlich zu Performance-Problemen beigetragen.
Dasselbe hier. Ich fand grunt-contrib-sass extrem langsam. Sobald ich auf postcss + grunt-sass umgestiegen bin, hat sich die Geschwindigkeit enorm verbessert.
Ich benutze Grunt und Gulp zusammen, weil Grunt viel mehr Pakete als Gulp hat. Dann bekommt man das Beste aus beiden Welten.
Guter Beitrag – große Grunt/Gulpfiles sind sicherlich schmerzhaft anzusehen. Ich habe den gleichen Ansatz bei Gulp verfolgt. Meine gulpfile sieht jetzt so aus
Mit einzelnen Aufgaben in eigenen Javascript-Dateien im Verzeichnis ./gulp-tasks. Viel besser zu handhaben!
Ich dachte, dieser Stil von `require dir` sei keine Best Practice und würde beim Upgrade auf Gulp 4 Kopfschmerzen verursachen.
Danke für den Beitrag, wir sind große Fans davon, unsere Aufgaben so aufzuteilen, wie wir es bei der Arbeit tun.
Es kann sogar noch weiter vereinfacht werden – Sie müssen `load-grunt-tasks` nicht installieren, da `load-grunt-config` es enthält.
Und die Datei Gruntfile.js kann so einfach werden wie
load-grunt-config sucht standardmäßig nach einem Ordner `grunt/` für Ihre Aufgaben, und Sie können trotzdem Optionen an load-grunt-tasks übergeben, wenn Sie dies wünschen.
Toller Beitrag, danke fürs Teilen. Kennt jemand die Vor- und Nachteile der Verwendung von `load-grunt-tasks` anstelle von `matchdep` zur Automatisierung des Ladens von Aufgaben?
require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);Toller Artikel, ich bin tatsächlich daran interessiert zu erfahren, ob dies mit jit-grunt funktionieren würde.
Ja, das funktioniert absolut.
Ich empfehle `jit-grun` dringend gegenüber `load-grunt-tasks`, da es die Initialisierung von Aufgaben erheblich beschleunigt.
Außerdem benötigen Sie `load-grunt-configs` nicht, um Aufgabendateien zu laden, da Grunt bereits einen Loader für externe Konfigurationen hat – `grunt.loadTasks` (Dokumentation).
Hier ist ein Beispiel aus unserer Konfigurationsdatei