Grunt Cheatsheet¶
Grunt - Die JavaScript Task Runner
Grunt ist ein JavaScript-Task-Läufer, der repetitive Aufgaben wie Minification, Compilation, Unit Testing und Linting automatisiert. Es verwendet einen Konfiguration-over-Code-Ansatz mit einem riesigen Ökosystem von Plugins, um praktisch jede Aufgabe zu bewältigen. < p>
Inhaltsverzeichnis¶
- [Installation](#installation
- (#getting-started)
- Gruntfile Konfiguration
- Core Tasks
- (#file-processing_)
- CSS Aufgaben
- JavaScript Aufgaben
- (#image-processing_)
- HTML Aufgaben
- Entwicklungsserver
- (#watch-tasks_)
- [Build Pipeline](#build-pipeline
- [Plugin Ecosystem](#plugin-ecosystem_
- Unterrichtsaufgaben
- Multi-target Aufgaben
- (#template-processing)
- (#testing-integration_)
- [Bestellung](LINK_17__
- (#performance-optimization)
- Beste Praktiken
Installation¶
Globale Installation¶
```bash
Install Grunt CLI globally¶
npm install -g grunt-cli
Verify installation¶
grunt --version ```_
Lokale Installation¶
```bash
Initialize npm project¶
npm init -y
Install Grunt locally¶
npm install --save-dev grunt
Install common plugins¶
npm install --save-dev grunt-contrib-uglify grunt-contrib-cssmin grunt-contrib-concat grunt-contrib-watch ```_
Projektaufbau¶
```bash
Create project structure¶
mkdir my-grunt-project cd my-grunt-project
Initialize package.json¶
npm init -y
Install Grunt and essential plugins¶
npm install --save-dev grunt grunt-contrib-uglify grunt-contrib-cssmin grunt-contrib-concat grunt-contrib-copy grunt-contrib-clean grunt-contrib-watch grunt-contrib-connect
Create Gruntfile¶
touch Gruntfile.js
Create source directories¶
mkdir -p src/{css,js,images,html} mkdir dist ```_
Paket.json Konfiguration¶
json
{
"name": "my-grunt-project",
"version": "1.0.0",
"description": "A Grunt-powered project",
"scripts": {
"build": "grunt build",
"dev": "grunt dev",
"watch": "grunt watch",
"serve": "grunt serve",
"test": "grunt test"
},
"devDependencies": {
"grunt": "^1.6.1",
"grunt-contrib-uglify": "^5.2.2",
"grunt-contrib-cssmin": "^4.0.0",
"grunt-contrib-concat": "^2.1.0",
"grunt-contrib-copy": "^1.0.0",
"grunt-contrib-clean": "^2.0.1",
"grunt-contrib-watch": "^1.1.0",
"grunt-contrib-connect": "^3.0.0",
"grunt-contrib-jshint": "^3.2.0",
"grunt-sass": "^3.1.0"
}
}_
Erste Schritte¶
Basic Gruntfile¶
```javascript // Gruntfile.js module.exports = function(grunt) {
// Project configuration grunt.initConfig({ pkg: grunt.file.readJSON('package.json'),
// Concatenate files
concat: {
js: {
src: ['src/js/**/*.js'],
dest: 'dist/js/bundle.js'
},
css: {
src: ['src/css/**/*.css'],
dest: 'dist/css/styles.css'
}
},
// Minify JavaScript
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
build: {
src: 'dist/js/bundle.js',
dest: 'dist/js/bundle.min.js'
}
},
// Minify CSS
cssmin: {
target: {
files: {
'dist/css/styles.min.css': ['dist/css/styles.css']
}
}
},
// Watch for changes
watch: {
js: {
files: ['src/js/**/*.js'],
tasks: ['concat:js', 'uglify']
},
css: {
files: ['src/css/**/*.css'],
tasks: ['concat:css', 'cssmin']
}
}
});
// Load plugins grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-cssmin'); grunt.loadNpmTasks('grunt-contrib-watch');
// Register tasks grunt.registerTask('default', ['concat', 'uglify', 'cssmin']); grunt.registerTask('build', ['concat', 'uglify', 'cssmin']); grunt.registerTask('dev', ['build', 'watch']); }; ```_
Aufgaben ausführen¶
```bash
Run default task¶
grunt
Run specific task¶
grunt uglify grunt cssmin
Run multiple tasks¶
grunt concat uglify
Run with options¶
grunt build --force grunt watch --verbose
List available tasks¶
grunt --help ```_
Projektstruktur¶
bash
my-grunt-project/
├── src/
│ ├── css/
│ │ ├── main.css
│ │ └── components/
│ ├── js/
│ │ ├── main.js
│ │ └── modules/
│ ├── images/
│ └── html/
├── dist/
│ ├── css/
│ ├── js/
│ ├── images/
│ └── index.html
├── Gruntfile.js
└── package.json_
Gruntfile Konfiguration¶
Konfigurationsstruktur¶
```javascript module.exports = function(grunt) { grunt.initConfig({ // Package information pkg: grunt.file.readJSON('package.json'),
// Global options
globalConfig: {
src: 'src',
dist: 'dist'
},
// Task configurations
taskName: {
options: {
// Global options for this task
},
target1: {
options: {
// Target-specific options
},
src: 'source/files',
dest: 'destination/file'
},
target2: {
// Another target configuration
}
}
}); }; ```_
Erweiterte Konfiguration¶
```javascript module.exports = function(grunt) {
// Configuration grunt.initConfig({ pkg: grunt.file.readJSON('package.json'),
// Global configuration
config: {
src: 'src',
dist: 'dist',
tmp: '.tmp'
},
// Clean task
clean: {
dist: ['<%= config.dist %>'],
tmp: ['<%= config.tmp %>']
},
// Copy task
copy: {
html: {
expand: true,
cwd: '<%= config.src %>',
src: '**/*.html',
dest: '<%= config.dist %>'
},
images: {
expand: true,
cwd: '<%= config.src %>/images',
src: '**/*',
dest: '<%= config.dist %>/images'
}
},
// Sass compilation
sass: {
options: {
implementation: require('sass'),
sourceMap: true
},
dist: {
files: {
'<%= config.dist %>/css/main.css': '<%= config.src %>/scss/main.scss'
}
}
},
// PostCSS processing
postcss: {
options: {
processors: [
require('autoprefixer')({browsers: 'last 2 versions'}),
require('cssnano')()
]
},
dist: {
src: '<%= config.dist %>/css/*.css'
}
}
});
// Load plugins require('load-grunt-tasks')(grunt);
// Custom tasks grunt.registerTask('build', [ 'clean:dist', 'copy', 'sass', 'postcss', 'uglify', 'cssmin' ]);
grunt.registerTask('default', ['build']); }; ```_
Vorlagenbearbeitung¶
```javascript grunt.initConfig({ pkg: grunt.file.readJSON('package.json'),
// Using templates concat: { options: { banner: '/! <%= pkg.name %> - v<%= pkg.version %> - ' + '<%= grunt.template.today("yyyy-mm-dd") %> */\n' }, dist: { src: ['src/js/*/*.js'], dest: 'dist/js/<%= pkg.name %>.js' } },
// Dynamic file mapping uglify: { dynamic: { files: [{ expand: true, cwd: 'src/js/', src: '**/*.js', dest: 'dist/js/', ext: '.min.js' }] } } }); ```_
Kernaufgaben¶
Dateioperationen¶
```javascript // Clean files and directories clean: { build: ['dist/'], tmp: ['.tmp/'], css: ['dist/css//*.css'], js: ['dist/js//*.js'] }
// Copy files copy: { main: { files: [ // Copy all files from src to dist { expand: true, cwd: 'src/', src: [''], dest: 'dist/' }, // Copy specific files { expand: true, cwd: 'src/assets/', src: ['fonts/', 'images/**'], dest: 'dist/assets/' } ] },
// Copy with renaming rename: { files: [{ expand: true, cwd: 'src/', src: ['**/*.txt'], dest: 'dist/', ext: '.bak' }] } }
// Concatenate files concat: { options: { separator: ';', stripBanners: true, banner: '/! <%= pkg.name %> - v<%= pkg.version %> */\n' }, js: { src: [ 'src/js/vendor/.js', 'src/js/modules/.js', 'src/js/main.js' ], dest: 'dist/js/bundle.js' }, css: { src: ['src/css/**/.css'], dest: 'dist/css/styles.css' } } ```_
Datei filtern¶
```javascript // File filtering and processing copy: { filterFiles: { files: [{ expand: true, cwd: 'src/', src: ['**/*'], dest: 'dist/', filter: function(filepath) { // Only copy files modified in last 24 hours return (Date.now() - fs.statSync(filepath).mtime) < 24*60*60*1000; } }] } }
// Conditional file processing uglify: { conditional: { files: [{ expand: true, cwd: 'src/js/', src: ['**/*.js'], dest: 'dist/js/', ext: '.min.js', filter: function(filepath) { // Only minify files larger than 1KB return fs.statSync(filepath).size > 1024; } }] } } ```_
CSS Aufgaben¶
Sass Compilation¶
```javascript // Sass compilation sass: { options: { implementation: require('sass'), sourceMap: true, outputStyle: 'expanded' },
// Development build dev: { options: { sourceMap: true, outputStyle: 'expanded' }, files: { 'dist/css/main.css': 'src/scss/main.scss' } },
// Production build prod: { options: { sourceMap: false, outputStyle: 'compressed' }, files: { 'dist/css/main.min.css': 'src/scss/main.scss' } },
// Multiple files multiple: { files: [{ expand: true, cwd: 'src/scss/', src: ['*.scss'], dest: 'dist/css/', ext: '.css' }] } }
// Less compilation less: { development: { options: { paths: ['src/less/includes'] }, files: { 'dist/css/main.css': 'src/less/main.less' } },
production: { options: { paths: ['src/less/includes'], cleancss: true, modifyVars: { imgPath: '"http://mycdn.com/path/to/images"', bgColor: 'red' } }, files: { 'dist/css/main.min.css': 'src/less/main.less' } } } ```_
CSS Verarbeitung¶
```javascript // CSS minification cssmin: { options: { mergeIntoShorthands: false, roundingPrecision: -1 }, target: { files: { 'dist/css/main.min.css': ['src/css/**/*.css'] } },
// Multiple targets multiple: { files: [{ expand: true, cwd: 'src/css/', src: ['.css', '!.min.css'], dest: 'dist/css/', ext: '.min.css' }] } }
// Autoprefixer autoprefixer: { options: { browsers: ['last 2 versions', 'ie 8', 'ie 9'] }, single_file: { src: 'dist/css/main.css', dest: 'dist/css/main.prefixed.css' }, multiple_files: { expand: true, flatten: true, src: 'dist/css/*.css', dest: 'dist/css/prefixed/' } }
// PostCSS postcss: { options: { map: true, processors: [ require('pixrem')(), require('autoprefixer')({browsers: 'last 2 versions'}), require('cssnano')() ] }, dist: { src: 'dist/css/*.css' } } ```_
CSS Futter¶
```javascript // CSS Lint csslint: { options: { csslintrc: '.csslintrc' }, strict: { options: { import: 2 }, src: ['src/css//*.css'] }, lax: { options: { import: false }, src: ['src/css//*.css'] } }
// Stylelint stylelint: { all: ['src/css/**/*.css'], options: { configFile: '.stylelintrc', formatter: 'string', ignoreDisables: false, failOnError: true, outputFile: '', reportNeedlessDisables: false, syntax: '' } } ```_
JavaScript Aufgaben¶
JavaScript Mining¶
```javascript // UglifyJS uglify: { options: { banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n', mangle: { reserved: ['jQuery', '$'] }, compress: { drop_console: true } },
// Single file single: { src: 'src/js/main.js', dest: 'dist/js/main.min.js' },
// Multiple files multiple: { files: [{ expand: true, cwd: 'src/js/', src: '**/*.js', dest: 'dist/js/', ext: '.min.js' }] },
// With source maps sourcemap: { options: { sourceMap: true, sourceMapName: 'dist/js/main.min.js.map' }, src: 'src/js/main.js', dest: 'dist/js/main.min.js' } }
// Terser (ES6+ minification) terser: { options: { ecma: 2015, compress: { drop_console: true }, output: { comments: false } }, main: { files: { 'dist/js/main.min.js': ['src/js/main.js'] } } } ```_
JavaScript Transpilation¶
```javascript // Babel transpilation babel: { options: { sourceMap: true, presets: ['@babel/preset-env'] }, dist: { files: [{ expand: true, cwd: 'src/js/', src: ['**/*.js'], dest: 'dist/js/', ext: '.js' }] } }
// TypeScript compilation typescript: { base: { src: ['src/ts/**/*.ts'], dest: 'dist/js/app.js', options: { module: 'amd', target: 'es5', sourceMap: true, declaration: true } } }
// CoffeeScript compilation coffee: { compile: { files: { 'dist/js/app.js': 'src/coffee/app.coffee' } },
multiple: { expand: true, flatten: true, cwd: 'src/coffee/', src: ['*.coffee'], dest: 'dist/js/', ext: '.js' } } ```_
JavaScript Futter¶
```javascript // JSHint jshint: { options: { curly: true, eqeqeq: true, eqnull: true, browser: true, globals: { jQuery: true } }, uses_defaults: ['src/js/**/*.js'], with_overrides: { options: { curly: false, undef: true }, files: { src: ['src/js/specific.js'] } } }
// ESLint eslint: { options: { configFile: '.eslintrc.js', rulePaths: ['conf/rules'] }, target: ['src/js/**/*.js'] }
// JSLint jslint: { client: { src: ['src/js/**/*.js'], directives: { browser: true, predef: ['jQuery'] } } } ```_
Browser-Integration¶
```javascript browserify: { dist: { files: { 'dist/js/bundle.js': ['src/js/main.js'] }, options: { transform: [['babelify', {presets: ['@babel/preset-env']}]] } },
// With external libraries vendor: { src: [], dest: 'dist/js/vendor.js', options: { require: ['jquery', 'underscore'] } },
app: { src: ['src/js/main.js'], dest: 'dist/js/app.js', options: { external: ['jquery', 'underscore'] } } }
// Webpack integration webpack: { options: { entry: './src/js/main.js', output: { path: 'dist/js/', filename: 'bundle.js' }, module: { rules: [{ test: /.js$/, use: { loader: 'babel-loader', options: { presets: ['@babel/preset-env'] } } }] } }, dev: { mode: 'development', devtool: 'source-map' }, prod: { mode: 'production' } } ```_
Bildbearbeitung¶
Bildoptimierung¶
```javascript // Image minification imagemin: { dynamic: { files: [{ expand: true, cwd: 'src/images/', src: ['**/*.{png,jpg,gif,svg}'], dest: 'dist/images/' }] },
// With options options: { optimizationLevel: 3, svgoPlugins: [{removeViewBox: false}], use: [mozjpeg({quality: 80})] } }
// Responsive images responsive_images: { dev: { options: { engine: 'im', sizes: [{ width: 320, suffix: '_small', quality: 80 }, { width: 640, suffix: '_medium', quality: 80 }, { width: 1024, suffix: '_large', quality: 80 }] }, files: [{ expand: true, src: ['src/images/**/*.{jpg,gif,png}'], cwd: 'src/', dest: 'dist/' }] } }
// WebP conversion webp: { files: { expand: true, cwd: 'src/images/', src: ['**/*.{png,jpg,jpeg}'], dest: 'dist/images/webp/', ext: '.webp' }, options: { binpath: require('webp-bin'), preset: 'photo', verbose: true, quality: 80, alphaQuality: 80, compressionMethod: 6, segments: 4, psnr: 42, sns: 50, filterStrength: 40, filterSharpness: 3, simpleFilter: true, partitionLimit: 50, analysisPass: 6, multiThreading: true, lowMemory: false, alphaMethod: 0, alphaFilter: 'best', alphaCleanup: true, noAlpha: false, lossless: false } } ```_
Sprite Generation¶
```javascript // CSS Sprites sprite: { all: { src: 'src/images/icons/*.png', dest: 'dist/images/spritesheet.png', destCss: 'dist/css/sprites.css', cssFormat: 'css', algorithm: 'binary-tree', padding: 2 } }
// SVG Sprites svgstore: { options: { prefix: 'icon-', svg: { viewBox: '0 0 100 100', xmlns: 'http://www.w3.org/2000/svg' } }, default: { files: { 'dist/images/icons.svg': ['src/images/icons/*.svg'] } } }
// Icon fonts webfont: { icons: { src: 'src/images/icons/*.svg', dest: 'dist/fonts/', destCss: 'dist/css/', options: { font: 'icons', types: 'eot,woff,woff2,ttf,svg', syntax: 'bem', engine: 'node', autoHint: false, execMaxBuffer: 1024 * 200, htmlDemo: true, destHtml: 'dist/' } } } ```_
HTML Aufgaben¶
HTML Processing¶
```javascript // HTML minification htmlmin: { dist: { options: { removeComments: true, collapseWhitespace: true, removeRedundantAttributes: true, removeScriptTypeAttributes: true, removeStyleLinkTypeAttributes: true, minifyJS: true, minifyCSS: true }, files: [{ expand: true, cwd: 'src/', src: ['**/*.html'], dest: 'dist/' }] } }
// HTML includes includes: { files: { src: ['src/pages/*.html'], dest: 'dist/', flatten: true, cwd: '.', options: { silent: true, includePath: 'src/includes/' } } }
// Template processing template: { dev: { options: { data: { title: 'Development Site', version: '<%= pkg.version %>', timestamp: '<%= grunt.template.today() %>' } }, files: [{ expand: true, cwd: 'src/templates/', src: ['**/*.html'], dest: 'dist/' }] } } ```_
HTML Validierung¶
```javascript // HTML validation htmlhint: { build: { options: { 'tag-pair': true, 'tagname-lowercase': true, 'attr-lowercase': true, 'attr-value-double-quotes': true, 'doctype-first': true, 'spec-char-escape': true, 'id-unique': true, 'head-script-disabled': true, 'style-disabled': true }, src: ['dist/**/*.html'] } }
// W3C validation validation: { options: { reset: grunt.option('reset') || false, stoponerror: false, remotePath: 'http://localhost:9001/', remoteFiles: ['index.html', 'about.html'], relaxerror: ['Bad value X-UA-Compatible for attribute http-equiv on element meta.'] }, files: { src: ['dist/**/*.html'] } } ```_
Entwicklungsserver¶
Server herunterladen¶
```javascript // Basic server connect: { server: { options: { port: 9000, hostname: 'localhost', base: 'dist', open: true } } }
// Advanced server configuration connect: { options: { port: 9000, hostname: 'localhost', livereload: 35729 },
livereload: { options: { open: true, middleware: function(connect) { return [ connect.static('.tmp'), connect().use('/bower_components', connect.static('./bower_components')), connect.static('src') ]; } } },
test: { options: { port: 9001, middleware: function(connect) { return [ connect.static('.tmp'), connect.static('test'), connect().use('/bower_components', connect.static('./bower_components')), connect.static('src') ]; } } },
dist: { options: { open: true, base: 'dist' } } }
// Proxy server connect: { proxies: [{ context: '/api', host: 'localhost', port: 3000, https: false, xforward: false, headers: { 'x-custom-added-header': 'value' }, hideHeaders: ['x-removed-header'] }],
server: { options: { port: 9000, middleware: function(connect, options) { var middlewares = []; var directory = options.directory || options.base[options.base.length - 1];
// Setup the proxy
middlewares.push(require('grunt-connect-proxy/lib/utils').proxyRequest);
// Serve static files
if (!Array.isArray(options.base)) {
options.base = [options.base];
}
options.base.forEach(function(base) {
middlewares.push(connect.static(base));
});
// Make directory browse-able
middlewares.push(connect.directory(directory));
return middlewares;
}
}
} } ```_
Live Reload¶
```javascript // Live reload configuration watch: { options: { livereload: true },
html: { files: ['src/**/*.html'], tasks: ['includes', 'htmlmin'] },
css: { files: ['src/css/**/*.css'], tasks: ['concat:css', 'cssmin'] },
js: { files: ['src/js/**/*.js'], tasks: ['jshint', 'concat:js', 'uglify'] },
livereload: { options: { livereload: '<%= connect.options.livereload %>' }, files: [ 'dist//*.html', 'dist/css//.css', 'dist/js/**/.js', 'dist/images/**/*.{png,jpg,jpeg,gif,webp,svg}' ] } }
// Browser Sync integration browserSync: { dev: { bsFiles: { src: [ 'dist/css/.css', 'dist/js/.js', 'dist/*.html' ] }, options: { watchTask: true, server: './dist' } } } ```_
Aufgaben ansehen¶
Basic Watch Konfiguration¶
```javascript watch: { // Watch CSS files css: { files: ['src/css/**/*.css'], tasks: ['concat:css', 'cssmin'], options: { spawn: false } },
// Watch JavaScript files js: { files: ['src/js/**/*.js'], tasks: ['jshint', 'concat:js', 'uglify'], options: { spawn: false } },
// Watch Sass files sass: { files: ['src/scss/**/*.scss'], tasks: ['sass', 'autoprefixer', 'cssmin'] },
// Watch HTML files html: { files: ['src/**/*.html'], tasks: ['includes', 'htmlmin'] },
// Watch images images: { files: ['src/images/**/*.{png,jpg,gif,svg}'], tasks: ['imagemin'] } } ```_
Erweiterte Uhrenmuster¶
```javascript watch: { options: { spawn: false, livereload: true },
// Conditional tasks based on file changes configFiles: { files: ['Gruntfile.js', 'package.json'], options: { reload: true } },
// Watch with different intervals slow: { files: ['src/large-files/**/*'], tasks: ['process-large-files'], options: { interval: 5000 } },
// Watch with custom event handling custom: { files: ['src/special/**/*'], tasks: [], options: { event: ['added', 'deleted'] } } }
// Dynamic watch configuration grunt.event.on('watch', function(action, filepath, target) { if (target === 'js') { // Only process the changed file grunt.config('jshint.single.src', filepath); grunt.config('uglify.single.src', filepath); grunt.config('uglify.single.dest', filepath.replace('src/', 'dist/').replace('.js', '.min.js')); } }); ```_
Uhr mit Fehlerbehandlung¶
```javascript watch: { options: { spawn: false, interrupt: true },
sass: { files: ['src/scss/**/*.scss'], tasks: ['sass'], options: { spawn: false, interrupt: true, event: ['added', 'deleted', 'changed'] } } }
// Error handling in tasks grunt.registerTask('safe-sass', function() { try { grunt.task.run('sass'); } catch (e) { grunt.log.error('Sass compilation failed: ' + e.message); grunt.fail.warn('Sass task failed, but continuing...'); } }); ```_
Pipeline erstellen¶
Entwicklung¶
```javascript // Development build pipeline grunt.registerTask('dev', [ 'clean:dist', 'jshint', 'sass:dev', 'autoprefixer', 'concat', 'copy:dev', 'includes', 'connect:livereload', 'watch' ]);
// Development build without server grunt.registerTask('build-dev', [ 'clean:dist', 'jshint', 'sass:dev', 'autoprefixer', 'concat', 'copy:dev', 'includes' ]); ```_
Produktionsaufbau¶
```javascript // Production build pipeline grunt.registerTask('build', [ 'clean:dist', 'jshint', 'sass:prod', 'autoprefixer', 'cssmin', 'concat', 'uglify', 'imagemin', 'copy:prod', 'includes', 'htmlmin', 'rev', 'usemin' ]);
// Build with testing grunt.registerTask('test-build', [ 'build', 'connect:test', 'qunit' ]);
// Complete production pipeline grunt.registerTask('production', [ 'build', 'test-build', 'compress', 'deploy' ]); ```_
Multi-Umwelt baut¶
```javascript // Environment-specific configurations grunt.initConfig({ env: { dev: { NODE_ENV: 'development' }, prod: { NODE_ENV: 'production' } },
// Conditional configuration uglify: { options: { compress: { drop_console: '<%= process.env.NODE_ENV === "production" %>' } }, build: { src: 'dist/js/bundle.js', dest: 'dist/js/bundle.min.js' } } });
// Environment-specific tasks grunt.registerTask('build-dev', ['env:dev', 'build-common']); grunt.registerTask('build-prod', ['env:prod', 'build-common']); grunt.registerTask('build-common', [ 'clean', 'sass', 'concat', 'uglify', 'copy' ]); ```_
Plugin Ecosystem¶
Grundlegende Plugins¶
```bash
File operations¶
npm install --save-dev grunt-contrib-clean grunt-contrib-copy grunt-contrib-concat
CSS processing¶
npm install --save-dev grunt-sass grunt-contrib-cssmin grunt-autoprefixer
JavaScript processing¶
npm install --save-dev grunt-contrib-uglify grunt-contrib-jshint grunt-babel
HTML processing¶
npm install --save-dev grunt-contrib-htmlmin grunt-includes
Image processing¶
npm install --save-dev grunt-contrib-imagemin grunt-responsive-images
Development¶
npm install --save-dev grunt-contrib-watch grunt-contrib-connect grunt-browser-sync
Build tools¶
npm install --save-dev grunt-usemin grunt-rev grunt-filerev ```_
Erweiterte Plugins¶
```javascript // File revisioning rev: { files: { src: ['dist/js//*.js', 'dist/css//*.css'] } }
// Asset injection usemin: { html: ['dist/**/*.html'], options: { assetsDirs: ['dist', 'dist/images'] } }
// Compression compress: { main: { options: { archive: 'dist.zip' }, files: [{ expand: true, cwd: 'dist/', src: ['**/*'], dest: '.' }] } }
// FTP deployment 'ftp-deploy': { build: { auth: { host: 'server.com', port: 21, authKey: 'key1' }, src: 'dist', dest: '/public_html', exclusions: ['dist//.DS_Store', 'dist//Thumbs.db'] } } ```_
Plugin geladen¶
```javascript // Load all grunt plugins require('load-grunt-tasks')(grunt);
// Load specific plugins grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-cssmin');
// Load plugins with pattern require('load-grunt-tasks')(grunt, { pattern: ['grunt-*', '!grunt-legacy-util'] });
// Load plugins from different locations grunt.loadTasks('custom-tasks'); ```_
Kundenspezifische Aufgaben¶
Grundlegende kundenspezifische Aufgaben¶
```javascript // Simple custom task grunt.registerTask('hello', 'Say hello', function() { grunt.log.writeln('Hello, World!'); });
// Task with parameters grunt.registerTask('greet', 'Greet someone', function(name) { if (!name) { grunt.log.writeln('Hello, stranger!'); } else { grunt.log.writeln('Hello, ' + name + '!'); } });
// Multi-task grunt.registerMultiTask('log', 'Log stuff', function() { grunt.log.writeln(this.target + ': ' + this.data); });
grunt.initConfig({ log: { foo: 'Hello', bar: 'World' } }); ```_
Erweiterte benutzerdefinierte Aufgaben¶
```javascript // Async task grunt.registerTask('async-task', 'An async task', function() { var done = this.async();
setTimeout(function() { grunt.log.writeln('Async task completed!'); done(); }, 1000); });
// Task with options grunt.registerTask('custom-build', function() { var options = this.options({ minify: true, sourcemap: false });
if (options.minify) { grunt.task.run('uglify'); }
if (options.sourcemap) { grunt.task.run('sourcemap'); } });
// File processing task grunt.registerMultiTask('process-files', function() { var options = this.options({ separator: '\n' });
this.files.forEach(function(file) { var src = file.src.filter(function(filepath) { if (!grunt.file.exists(filepath)) { grunt.log.warn('Source file "' + filepath + '" not found.'); return false; } else { return true; } }).map(function(filepath) { return grunt.file.read(filepath); }).join(options.separator);
grunt.file.write(file.dest, src);
grunt.log.writeln('File "' + file.dest + '" created.');
}); }); ```_
Aufgabenabhängigkeiten¶
```javascript // Task with dependencies grunt.registerTask('build-all', [ 'clean', 'jshint', 'sass', 'concat', 'uglify', 'cssmin' ]);
// Conditional task execution grunt.registerTask('conditional-build', function() { if (grunt.option('production')) { grunt.task.run(['build-prod']); } else { grunt.task.run(['build-dev']); } });
// Dynamic task creation grunt.registerTask('create-tasks', function() { var files = grunt.file.expand('src/modules/*.js');
files.forEach(function(file) { var taskName = 'process-' + path.basename(file, '.js');
grunt.registerTask(taskName, function() {
// Process individual file
grunt.log.writeln('Processing: ' + file);
});
}); }); ```_
Multi-Ziel Aufgaben¶
Konfiguration¶
```javascript grunt.initConfig({ // Multi-target task configuration uglify: { options: { banner: '/*! Global banner */\n' },
// Target 1: Main application
app: {
options: {
banner: '/*! App banner */\n'
},
src: 'src/js/app.js',
dest: 'dist/js/app.min.js'
},
// Target 2: Vendor libraries
vendor: {
src: ['src/js/vendor/*.js'],
dest: 'dist/js/vendor.min.js'
},
// Target 3: Dynamic files
dynamic: {
files: [{
expand: true,
cwd: 'src/js/',
src: '**/*.js',
dest: 'dist/js/',
ext: '.min.js'
}]
}
} });
// Run specific targets // grunt uglify:app // grunt uglify:vendor // grunt uglify (runs all targets) ```_
Kundenspezifisches Multi-Ziel Aufgaben¶
```javascript grunt.registerMultiTask('custom-process', 'Process files', function() { var options = this.options({ prefix: '', suffix: '' });
grunt.log.writeln('Processing target: ' + this.target);
this.files.forEach(function(file) { var contents = file.src.map(function(src) { return grunt.file.read(src); }).join('\n');
var processed = options.prefix + contents + options.suffix;
grunt.file.write(file.dest, processed);
grunt.log.writeln('Created: ' + file.dest);
}); });
grunt.initConfig({ 'custom-process': { options: { prefix: '/* Global prefix */\n' },
css: {
options: {
suffix: '\n/* CSS suffix */'
},
files: {
'dist/css/processed.css': ['src/css/**/*.css']
}
},
js: {
options: {
suffix: '\n/* JS suffix */'
},
files: {
'dist/js/processed.js': ['src/js/**/*.js']
}
}
} }); ```_
Vorlagenbearbeitung¶
Eingebaute Vorlagen¶
```javascript grunt.initConfig({ pkg: grunt.file.readJSON('package.json'),
// Using package.json data concat: { options: { banner: '/! <%= pkg.name %> - v<%= pkg.version %> - ' + '<%= grunt.template.today("yyyy-mm-dd") %> */\n' }, dist: { src: ['src/js/*/*.js'], dest: 'dist/js/<%= pkg.name %>.js' } },
// Date templates copy: { timestamped: { src: 'src/config.js', dest: 'dist/config-<%= grunt.template.today("yyyymmdd") %>.js' } } }); ```_
Benutzerdefinierte Vorlagen¶
```javascript // Custom template data grunt.initConfig({ config: { app: { name: 'MyApp', version: '1.0.0', author: 'John Doe' }, build: { timestamp: '<%= grunt.template.today("isoDateTime") %>', environment: process.env.NODE_ENV || 'development' } },
// Using custom data replace: { config: { src: ['src/js/config.js'], dest: 'dist/js/config.js', replacements: [{ from: '{{APP_NAME}}', to: '<%= config.app.name %>' }, { from: '{{VERSION}}', to: '<%= config.app.version %>' }, { from: '{{BUILD_TIME}}', to: '<%= config.build.timestamp %>' }] } } });
// Template processing task grunt.registerTask('process-templates', function() { var config = grunt.config('config');
grunt.file.expand('src/templates/**/*.html').forEach(function(file) { var content = grunt.file.read(file); var processed = grunt.template.process(content, { data: config });
var dest = file.replace('src/templates/', 'dist/');
grunt.file.write(dest, processed);
}); }); ```_
Integration testen¶
Einheitsprüfung¶
```javascript // QUnit qunit: { files: ['test/**/*.html'] }
// Mocha mocha: { test: { src: ['test/**/*.html'], options: { run: true, reporter: 'Spec' } } }
// Jasmine jasmine: { src: 'src/js//*.js', options: { specs: 'test/spec//.js', helpers: 'test/helpers/**/.js' } }
// Karma karma: { unit: { configFile: 'karma.conf.js' }, continuous: { configFile: 'karma.conf.js', singleRun: true, browsers: ['PhantomJS'] } } ```_
Code Coverage¶
```javascript // Istanbul coverage instrument: { files: 'src/js/**/*.js', options: { lazy: true, basePath: 'instrumented/' } }
storeCoverage: { options: { dir: 'coverage/' } }
makeReport: { src: 'coverage/**/*.json', options: { type: 'lcov', dir: 'coverage/', print: 'detail' } }
// Coverage task grunt.registerTask('coverage', [ 'clean:coverage', 'instrument', 'qunit', 'storeCoverage', 'makeReport' ]); ```_
End-to-End-Test¶
```javascript // Protractor protractor: { options: { configFile: 'protractor.conf.js', keepAlive: true, noColor: false }, e2e: { options: { args: { suite: 'e2e' } } } }
// WebDriver webdriver: { chrome: { tests: ['test/e2e/**/*.js'], options: { timeout: 10000, reporter: 'spec', browser: 'chrome' } } }
// Nightwatch nightwatch: { options: { standalone: true, jar_version: '2.53.0', jar_url: 'http://selenium-release.storage.googleapis.com/2.53/selenium-server-standalone-2.53.0.jar' } } ```_
Bereitstellung¶
Aufbau und Bereitstellung¶
```javascript // Build for deployment grunt.registerTask('deploy-build', [ 'clean', 'jshint', 'test', 'sass:prod', 'autoprefixer', 'cssmin', 'concat', 'uglify', 'imagemin', 'htmlmin', 'rev', 'usemin' ]);
// FTP deployment 'ftp-deploy': { production: { auth: { host: 'ftp.example.com', port: 21, authKey: 'production' }, src: 'dist/', dest: '/public_html/', exclusions: [ 'dist//.DS_Store', 'dist//Thumbs.db', 'dist/tmp' ] } }
// SFTP deployment sftp: { production: { files: { './': 'dist/**' }, options: { path: '/var/www/html/', host: 'example.com', username: 'deploy', privateKey: grunt.file.read('deploy_key'), showProgress: true } } } ```_
Git Bereitstellung¶
```javascript // Git deployment 'gh-pages': { options: { base: 'dist' }, src: ['**'] }
// Git push gitpush: { production: { options: { remote: 'production', branch: 'master' } } }
// Complete deployment pipeline grunt.registerTask('deploy', [ 'deploy-build', 'ftp-deploy:production' ]);
grunt.registerTask('deploy-github', [ 'deploy-build', 'gh-pages' ]); ```_
Einsatz von Docker¶
```javascript // Docker build shell: { docker_build: { command: 'docker build -t myapp:latest .' },
docker_push: { command: 'docker push myapp:latest' },
docker_deploy: { command: 'docker run -d -p 80:80 myapp:latest' } }
// Docker deployment task grunt.registerTask('deploy-docker', [ 'deploy-build', 'shell:docker_build', 'shell:docker_push', 'shell:docker_deploy' ]); ```_
Leistungsoptimierung¶
Parallele Verarbeitung¶
```javascript // Concurrent task execution concurrent: { dev: ['sass', 'coffee', 'jshint'], prod: ['cssmin', 'uglify', 'imagemin'], options: { logConcurrentOutput: true } }
// Use in build pipeline grunt.registerTask('build-fast', [ 'clean', 'concurrent:dev', 'concat', 'concurrent:prod' ]); ```_
Incremental Builds¶
```javascript // Newer files only newer: { options: { cache: '.grunt/newer' } }
// Use with tasks grunt.registerTask('build-incremental', [ 'newer:jshint', 'newer:sass', 'newer:imagemin' ]);
// Watch with newer watch: { js: { files: ['src/js/**/*.js'], tasks: ['newer:jshint', 'newer:uglify'] } } ```_
Caching¶
```javascript // Cache configuration cache: { options: { cache: '.grunt/cache' } }
// Cached image processing imagemin: { dynamic: { files: [{ expand: true, cwd: 'src/images/', src: ['**/*.{png,jpg,gif}'], dest: 'dist/images/' }] }, options: { cache: false // Disable cache for this task } } ```_
Best Practices¶
Projektorganisation¶
```javascript // Organized Gruntfile structure module.exports = function(grunt) {
// Time how long tasks take require('time-grunt')(grunt);
// Load all grunt tasks matching the ['grunt-', '@/grunt-*'] patterns require('load-grunt-tasks')(grunt);
// Configurable paths var config = { src: 'src', dist: 'dist', tmp: '.tmp' };
grunt.initConfig({ // Reference the config config: config,
// Load package.json
pkg: grunt.file.readJSON('package.json'),
// Task configurations
// ... (organized by type)
});
// Custom tasks grunt.registerTask('serve', function(target) { if (target === 'dist') { return grunt.task.run(['build', 'connect:dist:keepalive']); }
grunt.task.run([
'clean:server',
'concurrent:server',
'autoprefixer',
'connect:livereload',
'watch'
]);
});
grunt.registerTask('build', [ 'clean:dist', 'useminPrepare', 'concurrent:dist', 'autoprefixer', 'concat', 'cssmin', 'uglify', 'copy:dist', 'rev', 'usemin', 'htmlmin' ]);
grunt.registerTask('default', [ 'newer:jshint', 'test', 'build' ]); }; ```_
Konfigurationsmanagement¶
```javascript // External configuration var config = require('./grunt.config.js');
grunt.initConfig(config);
// Environment-specific configs var env = process.env.NODE_ENV || 'development'; var config = require('./config/' + env + '.js');
// Modular configuration grunt.initConfig({ // Load task configs sass: require('./grunt/sass.js'), uglify: require('./grunt/uglify.js'), watch: require('./grunt/watch.js') }); ```_
Fehlerbehebung¶
```javascript // Graceful error handling grunt.option('force', true); // Continue on errors
// Custom error handling grunt.registerTask('safe-build', function() { try { grunt.task.run(['jshint', 'sass', 'uglify']); } catch (e) { grunt.log.error('Build failed: ' + e.message); grunt.fail.warn('Build task failed, but continuing...'); } });
// Conditional error handling grunt.registerTask('conditional-fail', function() { if (grunt.option('strict')) { grunt.fail.fatal('Strict mode enabled, failing on any error'); } }); ```_
Performance Best Practices¶
- **Diese gleichzeitigen Aufgaben* für unabhängige Operationen
- **Inkrementelle Builds implementieren* mit
grunt-newer_ - **Cache teure Operationen* nach Möglichkeit
- Optimieren Sie die Uhrmuster, um unnötige Umbauten zu vermeiden
- Benutzen
spawn: false* in Uhrenaufgaben für schnellere Neuaufbauten
Best Practices der Wartung¶
- **Keep Plugins aktualisiert* regelmäßig
- ** Verwenden Sie semantische Versionierung* für Ihre Builds
- Dokumentieren Sie Ihre Aufgaben und Konfiguration
- **Implementieren Sie die richtige Fehlerbehandlung* *
- Umgebungsvariablen für sensible Daten
- ** Modulare Konfigurationsdateien erstellen* *
- Monitor Bauleistung mit
time-grunt_
--
Zusammenfassung¶
Grunt ist ein leistungsfähiger JavaScript-Task-Läufer, der bei der Automatisierung repetitiver Entwicklungsaufgaben durch Konfiguration übertrifft. Zu den wichtigsten Merkmalen gehören:
- **Configuration-driven*: JSON-basierte Konfiguration für einfaches Setup
- **Extensive Plugin Ecosystem*: Tausende von Plugins für jede Aufgabe
- **Mehrzweck Aufgaben*: Führen Sie verschiedene Konfigurationen für dieselbe Aufgabe aus
- Template System: Dynamische Konfiguration mit Vorlagen
- **File Processing*: Leistungsstarke Datei-Globing- und Verarbeitungsfunktionen
- **Watch und Live Reload*: Automatische Task-Ausführung bei Dateiänderungen
- **Kundenspezifische Aufgaben*: Einfache Erstellung von Sonderaufgaben und Workflows
- **Mature Ecosystem*: Mit umfangreichen Dokumentationen etabliert
Durch die Verwendung von Grunts konfigurationsgetriebenem Ansatz und riesigem Plugin-Ökosystem können Sie robuste, pflegefähige Build-Prozesse erstellen, die alles von einfacher Dateikonsultation bis hin zu komplexen Bereitstellungspipelines verarbeiten.
<= <= <= <================================================================================= Funktion copyToClipboard() {\cHFFFF} const commands = document.querySelectorAll('code'); alle Befehle = ''; Befehle. Für jede(cmd) => alle Befehle += cmd.textContent + '\n'); navigator.clipboard.writeText (allCommands); Alarm ('Alle Befehle, die in die Zwischenablage kopiert werden!'); }
Funktion generierenPDF() { Fenster.print(); }