Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Assets Build Automation
Search
Marconi Moreto
February 25, 2014
Programming
2
300
Assets Build Automation
Automated building of your webapp assets using Gruntjs.
Marconi Moreto
February 25, 2014
Tweet
Share
More Decks by Marconi Moreto
See All by Marconi Moreto
Development Mini Toolbox
marconi
1
170
Introduction to Docker
marconi
4
1.3k
SaltStack 101
marconi
10
1k
Concurrency with Gevent
marconi
3
400
Django Quickstart
marconi
1
110
Other Decks in Programming
See All in Programming
「テストは愚直&&網羅的に書くほどよい」という誤解 / Test Smarter, Not Harder
munetoshi
0
200
AIと”コードの評価関数”を共有する / Share the "code evaluation function" with AI
euglena1215
1
180
Vibe Codingの幻想を超えて-生成AIを現場で使えるようにするまでの泥臭い話.ai
fumiyakume
9
3.8k
ご注文の差分はこちらですか? 〜 AWS CDK のいろいろな差分検出と安全なデプロイ
konokenj
3
580
The Evolution of Enterprise Java with Jakarta EE 11 and Beyond
ivargrimstad
0
260
Claude Code派?Gemini CLI派? みんなで比較LT会!_20250716
junholee
1
530
はじめてのWeb API体験 ー 飲食店検索アプリを作ろうー
akinko_0915
0
140
PipeCDのプラグイン化で目指すところ
warashi
1
300
TypeScriptでDXを上げろ! Hono編
yusukebe
3
770
LT 2025-06-30: プロダクトエンジニアの役割
yamamotok
0
870
20250704_教育事業におけるアジャイルなデータ基盤構築
hanon52_
5
1.1k
PicoRuby on Rails
makicamel
2
140
Featured
See All Featured
VelocityConf: Rendering Performance Case Studies
addyosmani
332
24k
BBQ
matthewcrist
89
9.7k
Become a Pro
speakerdeck
PRO
29
5.4k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
34
3.1k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
507
140k
Code Reviewing Like a Champion
maltzj
524
40k
Keith and Marios Guide to Fast Websites
keithpitt
411
22k
Making the Leap to Tech Lead
cromwellryan
134
9.4k
Fashionably flexible responsive web design (full day workshop)
malarkey
407
66k
Docker and Python
trallard
45
3.5k
Imperfection Machines: The Place of Print at Facebook
scottboms
267
13k
GitHub's CSS Performance
jonrohan
1031
460k
Transcript
Assets Build Automation Marconi Moreto @marconimjr
Story time...
Classic way: <!doctype html> <html> <head> <title>My Awesome WebApp</title> <link
href="/static/css/somegrid.css" type="text/css" rel="stylesheet"/> <link href="/static/css/plugins.css" type="text/css" rel="stylesheet"/> <link href="/static/css/styles.css" type="text/css" rel="stylesheet"/> </head> <body> ... <script src="/static/js/jquery.js" type="text/javascript"></script> <script src="/static/js/plugins.js" type="text/javascript"></script> <script src="/static/js/app.js" type="text/javascript"></script> </body> </html> Number of requests: 7
None
Concatenate everything: <!doctype html> <html> <head> <title>My Awesome WebApp</title> <link
href="/static/css/app.css" type="text/css" rel="stylesheet"/> </head> <body> ... <script src="/static/js/app.js" type="text/javascript"></script> </body> </html> Number of requests: 3
Quick peak 1/2 body { padding: 0; margin: 0; }
#notification { width: 100%; font-size: 18px; text-align: center; padding: 15px 0; } #notification.success { background: #1abc9c; } #notification.error { background: #e74c3c; } app.css app.js /** * Basic notification class */ function Notification(msg, type) { this.msg = msg; this.type = typeof type !== 'undefined' ? type : 'success'; } Notification.prototype.show = function() { // build notification var notif = document.createElement('div'); notif.id = 'notification'; notif.className = this.type; notif.innerHTML = this.msg; // prepend notificaiton var doc = document.getElementsByTagName("body")[0]; doc.insertBefore(notif, doc.firstChild); }; $(function() { new Notification("Welcome!").show(); })(); Size: 277 bytes Size: 543 bytes
None
Minify everything: <!doctype html> <html> <head> <title>My Awesome WebApp</title> <link
href="/static/css/app.min.css" type="text/css" rel="stylesheet"/> </head> <body> ... <script src="/static/js/app.min.js" type="text/javascript"></script> </body> </html> ✓ Number of requests: 3 ✓ Faster loading time
Quick peak 2/2 body{padding:0;margin:0} #notification{width: 100%;font-size:18px;text- align:center;padding:15px 0} #notification.success{backg round:#1abc9c}
#notification.error{backgro und:#e74c3c} app.min.css app.min.js function Notification(a,b){this.msg=a,this.type="undefined"! =typeof b?b:"success"}Notification.prototype.show=function() {var a=document.createElement("div");a.id="notification",a.classNam e=this.type,a.innerHTML=this.msg;var b=document.getElementsByTagName("body") [0];b.insertBefore(a,b.firstChild)},$(function(){new Notification("Welcome!").show()})(); Size: 277 bytes Size: 177 bytes Size: 543 bytes Size: 352 bytes
None
Why do all these?
None
None
None
But why do we automate?
None
But really, why do we automate?
Focus on fun stuff!
The JavaScript Task Runner
✓ Node ✓ JS/Coffee + JSON ✓ Focus on web
development ✓ Over 2,294 plugins! Things to love about Grunt:
None
Who’s using it? And some more: Wordpress, Opera, etc.
$ npm install -g grunt-cli - package.json - Gruntfile.(js|coffee) Getting
started: Installation: Requirements:
{ "name": "MyAwesomeApp", "version": "0.0.1", "private": true } package.json $
npm install grunt --save $ npm install grunt-contrib-uglify --save $ npm install grunt-contrib-cssmin --save ... $ cat package.json { "name": "MyAwesomeApp", "version": "0.0.1", "private": true, "dependencies": { "grunt": "~0.4.2", "grunt-contrib-uglify": "~0.3.2", "grunt-contrib-cssmin": "~0.8.0" } } Install dependencies:
CSS Minification
module.exports = function(grunt) { grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), cssmin: { styles:
{ files: { 'static/css/app.min.css': ['src/css/*.css'] } } } }); grunt.loadNpmTasks('grunt-contrib-cssmin'); grunt.registerTask('default', ['cssmin']); }; Gruntfile.js Lets run it! $ grunt
JS Minification
module.exports = function(grunt) { grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), cssmin: { styles:
{ files: { 'static/css/app.min.css': ['src/css/*.css'] } } }, uglify: { scripts: { files: { 'static/js/app.min.js': [ 'src/js/jquery.js', 'src/js/plugins.js', 'src/js/app.js' ] } } } }); grunt.loadNpmTasks('grunt-contrib-cssmin'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.registerTask('default', ['cssmin']); grunt.registerTask('deploy', ['cssmin', 'uglify']); }; Gruntfile.js: Lets run it! $ grunt deploy
Concatenate on development + minify on deploy
module.exports = function(grunt) { grunt.initConfig({ ... concat: { dist: {
src: ['src/js/**/*.js'], dest: 'static/js/app.all.js' } }, uglify: { scripts: { files: { 'static/js/app.min.js': ['static/js/app.all.js'] } } } }); ... grunt.loadNpmTasks('grunt-contrib-concat'); grunt.registerTask('default', ['cssmin', 'concat']); grunt.registerTask('deploy', ['cssmin', 'concat', 'uglify']); }; Gruntfile.js: Lets run it! $ grunt deploy Install plugin: $ npm install grunt-contrib-concat --save
What about SASS? Updated project structure: $ tree . .
├── public │ └── index.html ├── src │ ├── Gruntfile.js │ ├── js │ │ ├── jquery.js │ │ ├── app.js │ │ └── plugins.js │ ├── package.json │ └── sass │ ├── _plugins.scss │ └── app.scss └── static ├── css │ └── app.min.css └── js ├── app.all.js └── app.min.js #notification { width: 100%; font-size: 18px; text-align: center; padding: 15px 0; &.success { background: #1abc9c; } &.error { background: #e74c3c; } } _plugins.scss body { padding: 0; margin: 0; } @import "plugins"; app.scss
module.exports = function(grunt) { grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), sass: { dist:
{ options: { outputStyle: 'compressed' }, files: { '../static/css/app.min.css': 'sass/app.scss' } } }, concat: { ... }, uglify: { ... } }); grunt.loadNpmTasks('grunt-sass'); grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.registerTask('default', ['sass', 'concat']); grunt.registerTask('deploy', ['sass', 'concat', 'uglify']); }; Gruntfile.js: Install plugin: $ npm install grunt-sass --save
Watching files
module.exports = function(grunt) { grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), ... watch: {
sass: { files: ['sass/**/*.scss'], tasks: ['sass'] }, concat: { files: ['js/**/*.js'], tasks: ['concat'] }, uglify: { files: ['../static/js/app.all.js'], tasks: ['uglify'] } } }); ... grunt.loadNpmTasks("grunt-contrib-watch"); ... }; Gruntfile.js: Install plugin: $ npm install grunt-contrib-watch --save Watch changes: $ grunt watch
<!doctype html> <html> <head> <title>My Awesome WebApp</title> <link href="/static/css/app.min.css" type="text/css"
rel="stylesheet"/> </head> <body> ... <script src="/static/js/app.min.js" type="text/javascript"></script> </body> </html> index.html
None
None
body { padding: 0; margin: 0; } #left, #right {
width: 290px; height: 315px; float: left; margin-right: 10px; } #left { background: url('../images/left.jpg') no-repeat top left; } #right { background: url('../images/right.jpg') no-repeat top left; } @import "plugins"; app.scss
<!doctype html> <html> <head> <title>My Awesome WebApp</title> <link href="/static/css/app.min.css" type="text/css"
rel="stylesheet"/> </head> <body> ... <script src="/static/js/app.min.js" type="text/javascript"></script> </body> </html> index.html Number of requests: 5
module.exports = function(grunt) { grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), sass: { ...
}, imageEmbed: { dist: { src: ['../static/css/app.min.css'], dest: '../static/css/app.min.css', options: {deleteAfterEncoding : false} } }, ... watch: { sass: { files: ['sass/**/*.scss'], tasks: ['sass', 'imageEmbed'] }, ... } }); grunt.loadNpmTasks("grunt-contrib-watch"); grunt.loadNpmTasks("grunt-image-embed"); grunt.registerTask('default', ['sass', 'imageEmbed', 'concat']); grunt.registerTask('deploy', ['sass', 'imageEmbed', 'concat', 'uglify']); }; Gruntfile.js: Install plugin: $ npm install grunt-image-embed --save
Number of requests: 3
OMG!
Thank you Marconi Moreto @marconimjr http://marconijr.com https://github.com/marconi