Bower, Grunt, and RequireJS are just a few tools that have been re-shaping the frontend development world, replacing cluttered script tags and server-side build solutions with a sophisticated, but sometimes complex approach to dependency management and module loading. In this talk, we'll put on our trendy frontend developer hat and find out how these tools work and how they differ from what we might be used to. Most important, we'll see how using tools like this might look in Symfony2 and how our application can be a friendly place for a frontend guy/gal.
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...
Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools
1. Deck the halls with:
Grunt, RequireJS & Bower
by your friend:
!
Ryan Weaver
@weaverryan
2. Who is this jolly guy?
The “Docs” guy
!
!
KnpLabs US - Symfony consulting, training, Kumbaya
!
Writer for KnpUniversity.com
screencasts
Husband of the much more talented
@leannapelham
@weaverryan
knplabs.com
github.com/weaverryan
7. Node.js for running
server-side JavaScript
!
RequireJS/AMD
!
Today
JavaScript task runners
(e.g. Grunt) for uglifying
and much more
!
SASS/LESS are very
mature and can compile
themselves
@weaverryan
8. Your friend Pablo from
ServerGrove is
redeveloping the SG control
panel with a pure-JS
fronted
@weaverryan
9. Can we continue to use
JavaScript like we have
in the past?
@weaverryan
20. Pages
* Homepage:
A) Has its own page-specific JS code
!
* New Event
A) Has its own page-specific JS code
B) Has page-specific CSS (event_form.css)
@weaverryan
22. Node.js
1) Executes JavaScript code
!
2) Adds extra functionality for using
JavaScript to deal with filesystems and other
things that make sense on a server
!
3) Similar to the “php” executable that lets
us interpret PHP code
@weaverryan
3
26. Node.js gives us the
ability to use JavaScript
as yet-another-serverside-scripting-language
27. npm
1) Composer for Node.js
2) Can be used to install things globally or
into your project (usually in a node_modules)
directory
3) Reads dependencies from a package.json file
@weaverryan
3
28. With Node.js and npm,
we can quickly create small
JavaScript files that use
external modules
29. With PHP and Composer,
we can quickly create small
PHP files that use
external libraries
34. Problem:
How do I get frontend libraries (e.g. jQuery,
Bootstrap) downloaded into my project?
http://www.flickr.com/photos/perry-pics/5251240991/
35. Bower
1) Downloads frontend libraries (usually JS)
into a directory in your project
2) Reads from a bower.json file
3) Handles dependencies!
@weaverryan
3
39. bower init
creates a bower.json file, with nothing
important in it
bower install bootstrap --save
Download the “bootstrap” library and
adds it as a dependency to bower.json
52. Problem:
Before we reference something in JavaScript, we
need to make sure it’s been included via a
<script> tag
http://www.flickr.com/photos/sewtechnicolor/8213938281/
53. RequireJS
* A library that allows us to load JavaScript
resources without worrying about script tags
!
* Instead, we use a require function, which is
quite similar to the PHP require statement
@weaverryan
54. RequireJS works by requiring “modules”,
not files.
(though one file will contain one module)
63. But how!
* All files are loaded by adding script tags
right into the HTML. But these use the async
tag, so do not block the page load.
!
* You’ll commonly see a data-main attribute
in setup. It loads that module. Our setup
does the same thing.
@weaverryan
67. app/homepage.js
define([], function() {
$ = require('jquery');
$('...');
});
The require function *can’t* work like this.
!
Unlike PHP files, scripts need to be
downloaded, which takes time.
!
Our function can’t run until that happens
68. app/homepage.js
define(['jquery'], function ($) {
$(document).ready(function() {
$('a').on('click', function(e) {
e.preventDefault();
alert('Ah ah ah, you didn't say
the magic word!');
})
});
});
Get the jquery module and *then* execute
this function
69. app/homepage.js
define(['jquery', 'bootstrap'], function ($,
Bootstrap) {
$(document).ready(function() {
$('a').on('click', function(e) {
e.preventDefault();
var $nope = $('<div>...</div>');
$nope.text('Ah ah ah, you didn't
say the magic word!'
);
$nope.modal();
})
});
Get the jquery and bootstrap modules
});
and *then* execute this function
70. base.html.twig
requirejs.config({
baseUrl: '/assets/js',
paths: {
jquery: '../vendor/jquery/jquery.min',
bootstrap: '../vendor/bootstrap/dist/js/bootstrap.min'
},
shim: {
fixes an issue where
bootstrap: ['jquery'] Bootstrap *needs* jQuery
}
before it’s downloaded
});
shim is a way for you to configure libraries
that aren’t proper RequireJS modules
71. Let’s create a new module (Love) that
takes down the grinch before he steals
Christmas.
http://en.wikipedia.org/wiki/File:The_Grinch_(That_Stole_Christmas).jpg
72. app/modules/love.js
define(['jquery', 'bootstrap'], function ($, Boots) {
return {
spiritOfXmas: function() {
$('a').on('click', function(e) {
e.preventDefault();
var $love = $('<div>...</div>');
$love.text('The Grinch’s heart grew
three sizes that day'
);
$nope.modal();
});
}
}
});
73. app/modules/love.js
define(['jquery', 'bootstrap'], function ($, Boots) {
return {
spiritOfXmas: function() {
$('a').on('click', function(e) {
e.preventDefault();
var $love = $('<div>...</div>');
$love.text('The Grinch’s heart grew
three sizes that day'
);
$nope.modal();
});
}
}
});
Return some value (usually an object) that
will be the app/modules/newman “module”
82. Problem:
Each module is loaded from an individual file
meaning there are lots of HTTP requests
83. Solution:
When we include the file containing moduleA,
let’s also packaged moduleB and moduleC in
there so when we need them, we already have
them.
84. Let’s start by creating a
common “module” that’s
always loaded
90. Because now we have a
module (common) that’s
*always* loaded
91. and we can use the
optimizer to “push” more
modules (e.g. bootstrap,
jquery) into it
92. Installing the Optimizer
* Optimization is a server-side JavaScript tool
!
* In other words it’s a node library installed
via npm
!
* We’ll install it into our project, by defining
our project’s dependencies in package.json
@weaverryan
96. and we also now have a
node_modules directory in our
project with requirejs in it
** We could have also installed it globally, like we did
with Bower. All we really need is the r.js executable
99. ({
mainConfigFile: 'web/assets/js/common.js',
baseUrl: './js',
appDir: 'web/assets',
dir: 'web/assets-built',
modules: web/assets directory is
The entire [
{
first copied to web/assets-built
name: 'common',
include: ['jquery', 'bootstrap']
},
{
name: 'app/homepage',
exclude: ['common']
}
]
})
100. ({
mainConfigFile: 'web/assets/js/common.js',
These files are then re-written.
baseUrl: reads their
RequireJS './js', dependencies
appDir: 'web/assets',
and includes them in the file
dir: 'web/assets-built',
automatically
modules: [
{
name: 'common',
include: ['jquery', 'bootstrap']
},
{
name: 'app/homepage',
exclude: ['common']
}
]
})
101. ({
mainConfigFile: 'web/assets/js/common.js',
baseUrl: './js',
... plus we can manually include
appDir: 'web/assets',
dir: modules
more 'web/assets-built',
modules: [
{
name: 'common',
include: ['jquery', 'bootstrap']
},
{
name: 'app/homepage',
exclude: ['common']
}
]
})
102. ({
mainConfigFile: 'web/assets/js/common.js',
baseUrl: './js',
appDir: 'web/assets',
dir: 'web/assets-built',
modules: [
{
Avoids packaging
name: 'common',
jquery , bootstrap
include: ['jquery', 'bootstrap']
into homepage since
},
we now already
{
have it in common
name: 'app/homepage',
exclude: ['common']
}
]
})
111. Compass
* Processes sass files into CSS
!
* A sass “framework”: adds a lot of extra
functionality, including CSS3 mixins, sprites,
etc
@weaverryan
112. Use Bower to bring in a sass Bootstrap
bower.json
{
"dependencies": {
"sass-bootstrap": "~3.0.0"
"requirejs": "~2.1.9",
}
}
114. Rename and reorganize CSS into SASS files
web/assets/sass/
* _base.scss
* _event.scss
* _events.scss
* event_form.scss
* layout.scss
115. Rename and reorganize CSS into SASS files
web/assets/sass/
* _base.scss
* _event.scss
* _events.scss
* event_form.scss
* layout.scss
(was event.css)
(was events.css)
(was main.css)
** these files were included on every page
116. Rename and reorganize CSS into SASS files
web/assets/sass/
* _base.scss
* _event.scss
* _events.scss
* event_form.scss
* layout.scss
(was event_form.css)
** included only on the “new event” page
117. Rename and reorganize CSS into SASS files
web/assets/sass/
* _base.scss
* _event.scss
EventBundle:Event:new.html.twig
* _events.scss
* event_form.scss
base.html.twig
* layout.scss
These are the only CSS files that will be
included directly
131. Problem:
We have an increasing number of “build”
operations we need to run for JavaScript & CSS
1) Before deploy, run the RequireJS optimizer
2) Before deploy, run Compass
3) During development, watch Compass
141. grunt.initConfig({
appDir: 'web/assets',
builtDir: 'web/assets-built',
We can setup
requirejs: {
main: {
variables and use
options: {
them
mainConfigFile: '<%= appDir %>/js/
common.js',
appDir: '<%= appDir %>',
baseUrl: './js',
dir: '<%= builtDir %>',
optimizeCss: "none",
optimize: "none",
modules: [... same as build.js ...]
}
}
142. grunt.initConfig({
appDir: 'web/assets',
builtDir: 'web/assets-built',
requirejs: { RequireJS *can* uglify CSS
and JS, but we’ll leave this
main: {
options: { to Uglify and Compass
mainConfigFile: '<%= appDir %>/js/
common.js',
appDir: '<%= appDir %>',
baseUrl: './js',
dir: '<%= builtDir %>',
optimizeCss: "none",
optimize: "none",
modules: [... same as build.js ...]
}
}
143. grunt.initConfig({
appDir: 'web/assets',
builtDir: 'web/assets-built', We can
This is a sub-task.
requirejs: {
now run grunt requirejs:main
main: {
or grunt requirejs to run all
options: {
sub-tasks '<%= appDir %>/js/
mainConfigFile:
common.js',
appDir: '<%= appDir %>',
baseUrl: './js',
dir: '<%= builtDir %>',
optimizeCss: "none",
optimize: "none",
modules: [... same as build.js ...]
}
}
169. If you wish, a fancier
solution exists, do it!
1) Extend the asset() function to change
the directory
!
2) Create a new Twig function to replace
asset()
170. Bower downloads JS
dependencies
!
Modules included via
RequireJS
Now
!
Compass compiles our
SASS files
!
@weaverryan
Grunt optimizes for
RequireJS, Uglifies, runs
Compass, and watches for
changes