Download - Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Transcript
Page 1: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Deck the halls with: Grunt, RequireJS & Bower

by your friend: !

Ryan Weaver @weaverryan

Page 2: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

The “Docs” guy !!KnpLabs US - Symfony consulting, training, Kumbaya !

Writer for KnpUniversity.com screencasts

knplabs.com github.com/weaverryan@weaverryan

Husband of the much more talented @leannapelham

Who is this jolly guy?

Page 3: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

@weaverryan

Shout-out to the Docs team!

Page 4: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

“Hack” with us on Sat!

@weaverryan

Page 5: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

!

!

Your friendly neighborhood JavaScript developer is all grown up… and kicking butt

Intro

Page 6: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

No Node.js !

Minifying and combining done with a backend solution !

No RequireJS, AngularJS !

SASS/LESS were virtually non-existent

@weaverryan

5 years ago

Page 7: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Node.js for running server-side JavaScript !

RequireJS/AMD !

JavaScript task runners (e.g. Grunt) for uglifying and much more !

SASS/LESS are very mature and can compile themselves

@weaverryan

Today

Page 8: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Your friend Pablo from ServerGrove is redeveloping the SG control panel with a pure-JS fronted

@weaverryan

Page 9: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Can we continue to use JavaScript like we have in the past?

@weaverryan

Page 10: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Maybe

@weaverryan

Page 11: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

But we’re Symfony2 Developers…

@weaverryan

Page 12: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

… with the highest standards in PHP

@weaverryan

Page 13: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

When we write JavaScript…

@weaverryan

Page 14: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Let’s write great JavaScript

@weaverryan

Page 15: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Our Goal

Take a traditional Symfony app and make it much more jolly by using Node.js, Bower, RequireJS, Compass and Grunt

Page 16: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

http://www.flickr.com/photos/calsidyrose/4183559218/

Node.js !

it’s on your Christmas list

Page 17: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

@weaverryan

* Symfony 2.3 - simple events website !

* The code: http://bit.ly/sfcon-js-github

The Project

Page 18: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

<head>{% block stylesheets %} {% stylesheets 'bundles/event/css/event.css' 'bundles/event/css/events.css' 'bundles/event/css/main.css' 'assets/vendor/bootstrap/dist/css/bootstrap.css' 'assets/vendor/bootstrap/dist/css/bootstrap-theme.css' output='css/generated/layout.css' %} <link rel="stylesheet" href="{{ asset_url }}" /> {% endstylesheets %}{% endblock %}</head>

base.html.twig

Page 19: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

<body>{% block body %}{% endblock %}!

{% block javascripts %} {% javascripts 'bundles/event/js/jquery.min.js' 'bundles/event/js/bootstrap.js' output='js/generated/main.js' %} <script type="text/javascript" src="{{ asset_url }}"></script> {% endjavascripts %}{% endblock %}</body>

base.html.twig

Page 20: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

@weaverryan

* 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)

Pages

Page 21: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

!

!

Server-side JavaScript

Node.js and npm

Page 22: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

@weaverryan

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

3

Node.js

Page 23: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

sys = require('sys');!

for (i=0; i<5; i++) { sys.puts(i);}

extra stuff added by Node.js

play.js

Page 24: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

play.js

Page 25: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

OMG!

http://www.flickr.com/photos/nocturnenoir/8305081285/

Page 26: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Node.js gives us the ability to use JavaScript as yet-another-server-side-scripting-language

Page 27: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

@weaverryan

1) Composer for Node.js

3

npm

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

Page 28: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

With Node.js and npm, we can quickly create small JavaScript files that use external modules

Page 29: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

With PHP and Composer, we can quickly create small PHP files that use external libraries

Page 30: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Why should we care?

Page 31: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Fronted JavaScript library build and development tools are written in JavaScript, executed with Node.js

Page 32: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Bower

Composer-lite for client-side JavaScript

Page 33: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Bower

(and one of those Node.js libraries installed with npm)

Page 34: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Problem:How do I get frontend libraries (e.g. jQuery, Bootstrap) downloaded into my project?

http://www.flickr.com/photos/perry-pics/5251240991/

Page 35: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

@weaverryan

1) Downloads frontend libraries (usually JS)

into a directory in your project

3

Bower

2) Reads from a bower.json file

3) Handles dependencies!

Page 36: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Installation !

sudo npm install -g bower

this means “install it globally” on your machine - i.e. a bit like how Pear works

Page 37: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools
Page 38: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

.bowerrc

Yo Bower, put the libraries over there:

{ "directory": "web/assets/vendor"}

Page 39: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

bower init

bower install bootstrap --save

creates a bower.json file, with nothing important in it

Download the “bootstrap” library and adds it as a dependency to bower.json

Page 40: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

{ "dependencies": { "bootstrap": "~3.0.2" }}

bower.json

** yes, this *is* composer.json for Bower

Page 41: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools
Page 42: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools
Page 43: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools
Page 44: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Now, how do we use these files?

Page 45: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

“Requiring” something in PHP

require 'Event.php';!

$event = new Event();echo $event->getName();

Page 46: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

“Requiring” something in JS

<script type="text/javascript" src="Event.js"></script>!

<script type="text/javascript"> console.log(Event.someMethod());</script>

Page 47: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Composer does 2 things:

1) Downloads libraries and their dependencies !

2) Sets up autoloading so you don’t need “require” statements

Page 48: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Bower does 1 thing:

1) Downloads libraries and their dependencies !

2) Sets up autoloading so you don’t need “require” statements

Page 49: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

<body>{% block body %}{% endblock %}!

{% block javascripts %} {% javascripts 'bundles/event/js/jquery.min.js' 'bundles/event/js/bootstrap.js' output='js/generated/main.js' %} <script type="text/javascript" src="{{ asset_url }}"></script> {% endjavascripts %}{% endblock %}</body>

before

Page 50: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

<body>{% block body %}{% endblock %}!

{% block javascripts %} {% javascripts 'assets/vendor/jquery/jquery.min.js' 'assets/vendor/bootstrap/dist/js/bootstrap.js' output='js/generated/main.js' %} <script type="text/javascript" src="{{ asset_url }}"></script> {% endjavascripts %}{% endblock %}</body>

after

Page 51: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

RequireJS!

!

Autoloading for client-side JavaScript

Page 52: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Before we reference something in JavaScript, we need to make sure it’s been included via a <script> tag

Problem:

http://www.flickr.com/photos/sewtechnicolor/8213938281/

Page 53: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

@weaverryan

* 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

RequireJS

Page 54: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

RequireJS works by requiring “modules”, not files.

(though one file will contain one module)

Page 55: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

bower install requirejs --save

Get it!

Page 56: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Remove all the JavaScript!<body>{% block body %}{% endblock %}!

{% block javascripts %} {% javascripts 'bundles/event/js/jquery.min.js' 'bundles/event/js/bootstrap.js' output='js/generated/main.js' %} <script type="text/javascript" src="{{ asset_url }}"></script> {% endjavascripts %}{% endblock %}</body>

Page 57: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

<script src="{{ asset('assets/vendor/requirejs/require.js') }}"></script>!<script>requirejs.config({ baseUrl: 'assets/js', paths: { jquery: '../vendor/jquery/jquery.min', bootstrap: '../vendor/bootstrap/dist/js/bootstrap.min' }});!require(['app/homepage']);</script>

base.html.twig

1) Bring in the require.js file downloaded via Bower using a normal script tag

Page 58: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

<script src="{{ asset('assets/vendor/requirejs/require.js') }}"></script>!<script>requirejs.config({ baseUrl: '/assets/js', paths: { jquery: '../vendor/jquery/jquery.min', bootstrap: '../vendor/bootstrap/dist/js/bootstrap.min' }});!require(['app/homepage']);</script>

2) Configure RequireJSAll modules live relative to this directory

base.html.twig

Page 59: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

<script src="{{ asset('assets/vendor/requirejs/require.js') }}"></script>!<script>requirejs.config({ baseUrl: '/assets/js', paths: { jquery: '../vendor/jquery/jquery.min', bootstrap: '../vendor/bootstrap/dist/js/bootstrap.min' }});!require(['app/homepage']);</script>

2) Configure RequireJSExceptions: when I ask for “jquery” look for it here (relative to baseUrl), instead of assets/js/jquery

base.html.twig

Page 60: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

<script src="{{ asset('assets/vendor/requirejs/require.js') }}"></script>!<script>requirejs.config({ baseUrl: '/assets/js', paths: { jquery: '../vendor/jquery/jquery.min', bootstrap: '../vendor/bootstrap/dist/js/bootstrap.min' }});!require(['app/homepage']);</script>

2) Configure RequireJS

Loads assets/js/app/homepage.js

base.html.twig

Page 61: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

app/homepage.js

define([], function() { console.log("It's alive!");});

Page 62: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

But how does it work?

Page 63: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

@weaverryan

* 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.

But how!

Page 64: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

now, what if we need jQuery?

Page 65: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

http://www.flickr.com/photos/danaberlith/4207059574

Remember, jQuery isn’t even downloaded yet - the global $ is not available

Page 66: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

define([], function() { $ = require('jquery'); $('...');});

… it might look something like this?

app/homepage.js

Page 67: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

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

app/homepage.js

Page 68: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

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

app/homepage.js

Page 69: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

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

app/homepage.js

Page 70: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

requirejs.config({ baseUrl: '/assets/js', paths: { jquery: '../vendor/jquery/jquery.min', bootstrap: '../vendor/bootstrap/dist/js/bootstrap.min' }, shim: { bootstrap: ['jquery'] }});

fixes an issue where Bootstrap *needs* jQuery before it’s downloaded

shim is a way for you to configure libraries that aren’t proper RequireJS modules

base.html.twig

Page 71: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

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

Page 72: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

app/modules/love.jsdefine(['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(); }); } }});

Page 73: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

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”

app/modules/love.js

Page 74: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

define( ['jquery', 'app/modules/love'], function ($, Love) {!

$(document).ready(function() { Love.spiritOfXmas(); });});

app/homepage.js

Page 75: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

This takes care of bringing in JavaScript for the homepage. But what about the new event page?

Page 76: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

1) Move the RequireJS code to a new template

::requirejs.html.twig

<script src="{{ asset('assets/vendor/requirejs/require.js') }}"></script><script>requirejs.config({ baseUrl: '/assets/js', paths: { domReady: '../vendor/requirejs-domready/domReady', jquery: '../vendor/jquery/jquery.min', bootstrap: '../vendor/bootstrap/dist/js/bootstrap.min' } // …});!require(['{{ module }}']);</script>

... and make the module a variable

Page 77: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

2) Add a requirejs block to your <head>

::base.html.twig

{% block requirejs %}{% endblock %}

Page 78: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

3) Include the module you need

EventBundle:Event:index.html.twig

{% block requirejs %} {{ include('::_requirejs.html.twig', { 'module': 'app/homepage'}) }}{% endblock %}

Page 79: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

4) Repeat!

EventBundle:Event:new.html.twig

{% block requirejs %} {{ include('::_requirejs.html.twig', { 'module': 'app/event_new'}) }}{% endblock %}

Page 80: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

app/event_new.js

define(['jquery'], function ($) {!

$(document).ready(function() { // ... });});

Page 81: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Optimization

Combining JavaScript files

Page 82: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Problem:

Each module is loaded from an individual file meaning there are lots of HTTP requests

Page 83: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

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.

Page 84: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Let’s start by creating a common “module” that’s always loaded

Page 85: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

requirejs.config({ paths: { domReady: '../vendor/requirejs-domready/domReady', jquery: '../vendor/jquery/jquery.min', bootstrap: '../vendor/bootstrap/dist/js/bootstrap.min' }, shim: { bootstrap: ['jquery'] }});

assets/js/common.js

Page 86: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

<script src="{{ asset('/assets/vendor/requirejs/require.js') }}"></script>!

<script> requirejs.config({ baseUrl: '/assets/js' });!

require(['common'], function (common) { require(['{{ module }}']); });</script>

::requirejs.html.twig

Page 87: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

<script src="{{ asset('/assets/vendor/requirejs/require.js') }}"></script>!

<script> requirejs.config({ baseUrl: '/assets/js' });!

require(['common'], function (common) { require(['{{ module }}']); });</script>

Setup baseUrl so we can reference the common module below

::requirejs.html.twig

Page 88: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

<script src="{{ asset('/assets/vendor/requirejs/require.js') }}"></script>!

<script> requirejs.config({ baseUrl: '/assets/js' });!

require(['common'], function (common) { require(['{{ module }}']); });</script>

Load common and *then* load our real module

::requirejs.html.twig

Page 89: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Why?http://www.flickr.com/photos/danaberlith/4207059574

Page 90: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Because now we have a module (common) that’s *always* loaded

Page 91: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

and we can use the optimizer to “push” more modules (e.g. bootstrap, jquery) into it

Page 92: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

@weaverryan

* 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

Installing the Optimizer

Page 93: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

npm init

Create an empty package.json

Page 94: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

npm install requirejs --save-dev

Page 95: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

{ "devDependencies": { "requirejs": "~2.1.9" }}

package.json

Page 96: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

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

Page 97: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

build.js

Configuration tells RequireJS how to minify and combine files

Page 98: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

({ mainConfigFile: 'web/assets/js/common.js', baseUrl: './js', appDir: 'web/assets', dir: 'web/assets-built', modules: [ { name: 'common', include: ['jquery', 'bootstrap'] }, { name: 'app/homepage', exclude: ['common'] } ]})

Page 99: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

({ mainConfigFile: 'web/assets/js/common.js', baseUrl: './js', appDir: 'web/assets', dir: 'web/assets-built', modules: [ { name: 'common', include: ['jquery', 'bootstrap'] }, { name: 'app/homepage', exclude: ['common'] } ]})

The entire web/assets directory is first copied to web/assets-built

Page 100: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

({ mainConfigFile: 'web/assets/js/common.js', baseUrl: './js', appDir: 'web/assets', dir: 'web/assets-built', modules: [ { name: 'common', include: ['jquery', 'bootstrap'] }, { name: 'app/homepage', exclude: ['common'] } ]})

These files are then re-written. RequireJS reads their dependencies and includes them in the file automatically

Page 101: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

({ mainConfigFile: 'web/assets/js/common.js', baseUrl: './js', appDir: 'web/assets', dir: 'web/assets-built', modules: [ { name: 'common', include: ['jquery', 'bootstrap'] }, { name: 'app/homepage', exclude: ['common'] } ]})

... plus we can manually include more modules

Page 102: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

({ mainConfigFile: 'web/assets/js/common.js', baseUrl: './js', appDir: 'web/assets', dir: 'web/assets-built', modules: [ { name: 'common', include: ['jquery', 'bootstrap'] }, { name: 'app/homepage', exclude: ['common'] } ]})

Avoids packaging jquery , bootstrap into homepage since we now already have it in common

Page 103: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

node node_modules/.bin/r.js -o build.js

Page 104: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools
Page 105: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Now, just point everything to assets-built

Page 106: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

{% set assetsPath = 'assets-built' %}!

<script src="{{ asset(assetsPath~'/vendor/requirejs/require.js') }}"></script><script>!

requirejs.config({ baseUrl: '/{{ assetsPath }}/js' });!

require(['common'], function (common) { require(['{{ module }}']);!

});</script>

Page 107: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Not super dynamic yet... but it works!

Page 108: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

assets-built is the same as assets

except when we include the common module, it has jquery and bootstrap packaged inside it

Page 109: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Compass

Sass with style

Page 110: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Problem:

Static CSS files are *so* 2010

http://www.flickr.com/photos/stevendepolo/8409407391

Page 111: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

@weaverryan

* Processes sass files into CSS

!

* A sass “framework”: adds a lot of extra

functionality, including CSS3 mixins, sprites,

etc

Compass

Page 112: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

{ "dependencies": { "sass-bootstrap": "~3.0.0" "requirejs": "~2.1.9", }}

bower.json

Use Bower to bring in a sass Bootstrap

Page 113: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

bower install

Page 114: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Rename and reorganize CSS into SASS files

web/assets/sass/ * _base.scss * _event.scss * _events.scss * event_form.scss * layout.scss

Page 115: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

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

Page 116: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

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

Page 117: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Rename and reorganize CSS into SASS files

web/assets/sass/ * _base.scss * _event.scss * _events.scss * event_form.scss * layout.scss

These are the only CSS files that will be included directly

EventBundle:Event:new.html.twig

base.html.twig

Page 118: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

{% block stylesheets %} <link rel="stylesheet" href="{{ asset('assets/css/layout.css') }}"/>{% endblock %}

{% block stylesheets %} {{ parent() }}!

<link rel="stylesheet" href="{{ asset('assets/css/event_form.css') }}"/>{% endblock %}

base.html.twig

EventBundle:Event:new.html.twig

Page 119: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

{% block stylesheets %} <link rel="stylesheet" href="{{ asset('assets/css/layout.css') }}"/>{% endblock %}

{% block stylesheets %} {{ parent() }}!

<link rel="stylesheet" href="{{ asset('assets/css/event_form.css') }}"/>{% endblock %}

We link directly to non-existent CSS files, which we’ll generate

base.html.twig

EventBundle:Event:new.html.twig

Page 120: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

@import "base";@import "../vendor/sass-bootstrap/lib/bootstrap";@import "event";@import "events";!

body { // ...}

layout.scss

Page 121: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

@import "base";@import "../vendor/sass-bootstrap/lib/bootstrap";@import "event";@import "events";!

body { // ...}

Sets variables and imports mixins used in all SASS files

layout.scss

Page 122: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

@import "base";@import "../vendor/sass-bootstrap/lib/bootstrap";@import "event";@import "events";!

body { // ...}Import other files that contain actual CSS rules. These will eventually be concatenated into 1 file.

layout.scss

Page 123: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

@import "base";!

/* for play, make the inputs super-rounded */.form-group input { @include border-radius(20px, 20px);}

event_form.scss

Page 124: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Now, just use more tools

Page 125: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

sudo npm install -g compass

Page 126: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

compass compile \ --css-dir=web/assets/css \ --sass-dir=web/assets/sass

Page 127: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

“partials” (files beginning with “_”) are ignored

Page 128: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

compass watch \ --css-dir=web/assets/css \ --sass-dir=web/assets/sass

Page 129: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

watches for file changes and regenerates the necessary CSS files

Page 130: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Grunt

app/console for JavaScript

Page 131: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

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

Page 132: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

sudo npm install -g grunt-cli

Install the Grunt executable

Page 133: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

{ "devDependencies": { "requirejs": "~2.1.9", "grunt": "~0.4.2", "grunt-contrib-jshint": "~0.6.3", "grunt-contrib-uglify": "~0.2.2", "grunt-contrib-requirejs": "~0.4.1", "grunt-contrib-compass": "~0.6.0", "grunt-contrib-watch": "~0.5.3" }}

package.json

Page 134: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

{ "devDependencies": { "requirejs": "~2.1.9", "grunt": "~0.4.2", "grunt-contrib-jshint": "~0.6.3", "grunt-contrib-uglify": "~0.2.2", "grunt-contrib-requirejs": "~0.4.1", "grunt-contrib-compass": "~0.6.0", "grunt-contrib-watch": "~0.5.3" }}

Install Grunt into your project + some modules

Page 135: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

npm install

Page 136: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Grunt works by creating a Gruntfile.js file, where we define tasks (like app/console)

Page 137: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

module.exports = function (grunt) { grunt.initConfig({ });!

grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-requirejs'); grunt.loadNpmTasks('grunt-contrib-compass'); grunt.loadNpmTasks('grunt-contrib-watch');};

Gruntfile.js

Page 138: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

grunt -h

Eventually we can run grunt RequireJS but we need to configure each command

Page 139: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Remove the RequireJS build.js and moves its contents here

Gruntfile.js

Use Grunt to run the RequireJS optimizer

Page 140: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

grunt.initConfig({ appDir: 'web/assets', builtDir: 'web/assets-built', requirejs: { main: { options: { mainConfigFile: '<%= appDir %>/js/common.js', appDir: '<%= appDir %>', baseUrl: './js', dir: '<%= builtDir %>', optimizeCss: "none", optimize: "none", modules: [... same as build.js ...] } } }

Page 141: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

grunt.initConfig({ appDir: 'web/assets', builtDir: 'web/assets-built', requirejs: { main: { options: { mainConfigFile: '<%= appDir %>/js/common.js', appDir: '<%= appDir %>', baseUrl: './js', dir: '<%= builtDir %>', optimizeCss: "none", optimize: "none", modules: [... same as build.js ...] } } }

We can setup variables and use them

Page 142: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

grunt.initConfig({ appDir: 'web/assets', builtDir: 'web/assets-built', requirejs: { main: { options: { mainConfigFile: '<%= appDir %>/js/common.js', appDir: '<%= appDir %>', baseUrl: './js', dir: '<%= builtDir %>', optimizeCss: "none", optimize: "none", modules: [... same as build.js ...] } } }

RequireJS *can* uglify CSS and JS, but we’ll leave this to Uglify and Compass

Page 143: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

grunt.initConfig({ appDir: 'web/assets', builtDir: 'web/assets-built', requirejs: { main: { options: { mainConfigFile: '<%= appDir %>/js/common.js', appDir: '<%= appDir %>', baseUrl: './js', dir: '<%= builtDir %>', optimizeCss: "none", optimize: "none", modules: [... same as build.js ...] } } }

This is a sub-task. We can now run grunt requirejs:main or grunt requirejs to run all sub-tasks

Page 144: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools
Page 145: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Repeat for Compass!

Page 146: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

compass: { dist: { options: { sassDir: '<%= builtDir %>/sass', cssDir: '<%= builtDir %>/css', environment: 'production', outputStyle: 'compressed' } }, dev: { options: { sassDir: '<%= appDir %>/sass', cssDir: '<%= appDir %>/css', outputStyle: 'expanded' } }}

Page 147: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools
Page 148: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

We have 2 sub-tasks: 1) compass:dist for deployment 2) compass:dev for development

Page 149: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Repeat for Uglify (to minimize our JS files)!

** The RequireJS optimizer can uglify, but using uglify directly gives us a bit more control

Page 150: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

uglify: { build: { files: [ { expand: true, cwd: '<%= builtDir %>', src: 'js/*.js', dest: '<%= builtDir %>' } ] }},

Page 151: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools
Page 152: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

And even JsHint

Page 153: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

jshint: { all: [ 'Gruntfile.js', '<%= appDir %>/js/{,*/}*.js' ]},

Page 154: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools
Page 155: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Roll these up into some grouped commands

http://www.flickr.com/photos/gazeronly/8206753938

Page 156: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

// task for developmentgrunt.registerTask('dev', [ 'jshint', 'compass:dev']);!

// task for before deploymentgrunt.registerTask('production', [ 'jshint', 'requirejs', 'uglify', 'compass:dist']);

Page 157: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

!

!

!

!

!

!

// task for before deploymentgrunt.registerTask('production', [ 'jshint', 'requirejs', 'uglify', 'compass:dist']);

1) 2) 3) 4)

1) syntax and style check our JS 2) copies assets to assets-dist and compiles some files 3) uglifies all JS files in assets-dist 4) compiles all assets-dist/sass files

Page 158: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

What about “watching”

Page 159: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

watch: { scripts: { files: [ '<%= appDir %>/js/*.js', // ... ], tasks: ['jshint'] }, compass: { files: '<%= appDir %>/sass/*.scss', tasks: ['compass:dev'] }}

Page 160: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools
Page 161: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

assets versus assets-dist

How to handle in Symfony

Page 162: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Problem:

Grunt perfectly copies assets to assets-dist and processes it. But how do we load our JS and CSS files from the correct directory?

Page 163: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Simple Solution

Page 164: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

parameters: assets_directory: 'assets'!

twig: # ... globals: assetsPath: %assets_directory%

config.yml

Page 165: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

parameters: assets_directory: 'assets-prod'

config_prod.yml

Page 166: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

<script src="{{ asset(assetsPath~'/vendor/requirejs/require.js') }}"></script><script> requirejs.config({ baseUrl: '/{{ assetsPath }}/js' });!

// ...</script>

::requirejs.html.twig

Page 167: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

{% block stylesheets %}<link rel="stylesheet" href="{{ asset(assetsPath~'/css/layout.css') }}"/>{% endblock %}

::base.html.twig

Page 168: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Manual, but straightforward

Page 169: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

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()

Page 170: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Bower downloads JS dependencies !

Modules included via RequireJS !

Compass compiles our SASS files !

Grunt optimizes for RequireJS, Uglifies, runs Compass, and watches for changes@weaverryan

Now

Page 171: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

When developing: !

grunt watch

Page 172: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

When deploying: !

grunt production

Page 173: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

and make your own Grunt tasks for other processing

Page 174: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

grunt.registerTask('symfonycon', function() { sys = require('sys'); sys.puts('Thanks everyone!');});

Page 175: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

JavaScript is a first-class tool in your stack

@weaverryan

Page 176: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Treat it with the same care and quality as everything else

@weaverryan

Page 177: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

And (code) be cool like a frontend developer

@weaverryan

Page 178: Cool like a Frontend Developer: Grunt, RequireJS, Bower and other Tools

Ho ho ho, thanks!

@weaverryan

Brutal Feedback appreciated https://joind.in/10372

The code: http://bit.ly/sfcon-js-github

Keep learning: KnpUniversity.com