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
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Marconi Moreto
February 25, 2014
Programming
2
310
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
1.1k
Concurrency with Gevent
marconi
3
410
Django Quickstart
marconi
1
120
Other Decks in Programming
See All in Programming
OCaml 5でモダンな並列プログラミングを Enjoyしよう!
haochenx
0
140
プロダクトオーナーから見たSOC2 _SOC2ゆるミートアップ#2
kekekenta
0
210
Fragmented Architectures
denyspoltorak
0
150
SourceGeneratorのススメ
htkym
0
200
CSC307 Lecture 08
javiergs
PRO
0
670
Grafana:建立系統全知視角的捷徑
blueswen
0
330
IFSによる形状設計/デモシーンの魅力 @ 慶應大学SFC
gam0022
1
300
AIによる高速開発をどう制御するか? ガードレール設置で開発速度と品質を両立させたチームの事例
tonkotsuboy_com
7
2.3k
Best-Practices-for-Cortex-Analyst-and-AI-Agent
ryotaroikeda
1
100
登壇資料を作る時に意識していること #登壇資料_findy
konifar
4
1.1k
LLM Observabilityによる 対話型音声AIアプリケーションの安定運用
gekko0114
2
430
コマンドとリード間の連携に対する脅威分析フレームワーク
pandayumi
1
450
Featured
See All Featured
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
1.8k
New Earth Scene 8
popppiees
1
1.5k
We Have a Design System, Now What?
morganepeng
54
8k
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
430
My Coaching Mixtape
mlcsv
0
48
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
190
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
231
22k
Agile that works and the tools we love
rasmusluckow
331
21k
[SF Ruby Conf 2025] Rails X
palkan
1
750
Darren the Foodie - Storyboard
khoart
PRO
2
2.4k
Code Review Best Practice
trishagee
74
20k
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
120
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