One thing is certain: times sure have changed. Not too long ago, front-end development, though not simple, was manageable. Learn HTML, a bit of CSS, and you’re on your way. These days, however, for lack of better words, there are far more variables to juggle.
Preprocessors, performance tuning, testing, image optimization, and minification represent but just a few of the key factors that the modern day front-end developer must keep in mind.
For instance, though it’s easy to use, CSS certainly does not scale well. And, while powerful, JavaScript can, at times, be an ugly, difficult language to work with. Then there’s the performance aspect; no longer are we merely designing for Internet Explorer and Firefox. No, these days, we have a myriad of browsers, devices, resolutions, and connection speeds to consider when developing new applications.
To say that ours is an incredibly tough industry would be the understatement of the century.
The upside is that, for every road block, solutions have certainly been provided.
The upside is that, for every road block, solutions have certainly been provided by members of the community. Consider the CSS scaling issue; well, preprocessors, like Sass, Less, and Stylus were introduced to drastically make our lives easier. What about the nasty CSS3 browser-prefixing issue? Compass takes care of that! And the JavaScript dilemma? Once again, CoffeeScript and, now, TypeScript to the rescue! The only problem is that each new solution requires its own system and process. As one might expect, over time, this can significantly complicate your workflow. Now, we have multiple Terminal tabs open, each monitoring a subset of the files within our projects, listening for changes. And that’s just the tip of the iceberg. We haven’t yet touched on workflow, coding best practices, image optimization, testing, and developing an automated build process. Even writing about all of these steps is shortening my breath! Wouldn’t it be fantastic if somebody wrapped up all of these preprocessors and best practices into one easy to use package?
Say Hello to Yeoman
Created by some of the friendly folks at Google (including Paul Irish and Addy Osmani), Yeoman is the solution to your problems. As the core team puts it, Yeoman offers an opinionated workflow to get you up and running with new projects as quickly as possible. But what does this really mean? Well, it offers the ability to scaffold new projects, as well as the necessary frameworks and testing tools. What this essentially translates to is less tedious configuration, and more creation.
To get started with Yeoman, we first need to install it from the command line. Run the following command:
curl -L get.yeoman.io | bash
This script will perform variety of things, including installing the necessary libraries for Yeoman to do its job. You’ll likely find that it requires a handful of steps on your part, but, don’t worry; it’ll tell you exactly what needs to be done!
Once the installation completes, run yeoman
to see what’s available. You’ll find a variety of options, such as init
, for initializing a new project, build
, for creating a special, optimized dist
folder for deployment, and install
, which makes the process of dealing with package management as easy as possible.
To learn more about what each options does, append
--help
to the command:yeoman init --help
.
Let’s create a new project with Yeoman. Create a new directory on your desktop, cd
to it from the Terminal, and run:
yeoman init
At this point, you’ll be prompted with a handful of questions.
- Would you like to include Twitter Bootstrap for Compass?
- Would you like to include the Twitter Bootstrap plugins?
- Would you like to include RequireJS (for AMD support)?
- Would you like to support writing ECMAScript 6 modules?
These questions give you the ability to configure your new project right out of the box. For now, choose “No” to each question.
If you’d prefer to bypass these questions in the future, instead run
yeoman init quickstart
. This will prepare a new application, with Modernizr, jQuery, and HTML5 Boilerplate baked in.
With that single command alone, Yeoman instantly scaffolds a new project for you. Don’t be overwhelmed by all of these files, though; if they weren’t generated for you, you’d eventually create them manually. Just think of Yeoman as the helpful robot, who does all of the manual labor for you.
“Yo, man; go fetch me jQuery and Modernizr!”
Now that we have a new project, let’s launch a preview server, and begin monitoring the application for changes.
yeoman server
Instantly, Google Chrome will be launched, displaying your project (also, no more security errors). Well, that’s handy, but, as you’ll quickly find, there’s much, much more to see. Place your browser and editor side-by-side, and try the following things:
LiveReloading
Change the h1
tag’s text, and watch it instantly update in the browser, without a refresh. Yeoman at your service! It achieves this, via the LiveReload Google Chrome extension, but, if that’s not installed, a fallback reload process will be used.
Sass
Change main.css
to main.sass
(or main.scss
, if that’s your preference), and enjoy instant compiling and updating in the browser. To test it out, try creating and using a variable.
// main.sass $textColor: #666 body color: $textColor
Nice! Zero setup required. You can now separate your stylesheets, as needed, and import them into main.sass
.
// main.sass @import 'grid' @import 'buttons' @import 'module'
Each time a file is saved, Yeoman will automatically re-compile your Sass into regular CSS, and refresh the browser.
Compass
If you’re a Sass fan, then it’s likely that you also prefer the excellent Compass framework. No worries; Yeoman is happy to oblige. Compass support is already available; simply import the applicable modules, and continue as usual.
// main.sass @import 'compass/css' * +box-sizing(border-box) .box width: 200px +transition(width 1s) &:hover width: 400px
If you’re not yet a preprocessor convert, you have to admit: this is significantly better than the alternative:
* { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .box { width: 200px; -webkit-transition: width 1s; -moz-transition: width 1s; -ms-transition: width 1s; -o-transition: width 1s; transition: width 1s; } .box:hover { width: 400px; }
CoffeeScript
JavaScript is just fine and dandy, but some feel that CoffeeScript provides a considerably cleaner syntax that fills in many of the gaps in the language (at least visually).
Within the scripts/
directory, optionally create a new folder, coffee/
, and add your first CoffeeScript file: person.coffee
.
# scripts/coffee/person.coffee class Person
Save the file, and, like magic, Yeoman immediately compiles it into vanilla JavaScript, and places the new file directly within the parent scripts/
directory. See for yourself:
// scripts/person.js var Person; Person = (function() { function Person() {} return Person; })();
Perfect, and more importantly, effortless!
If you need to modify the directory structure in any way, refer to the
gruntfile.js
file within the root of your application. Behind the scenes, Ben Alman’s Grunt tool is what configures the compilation.
At this point alone, Yeoman has given us a great deal of flexibility. With that single yeoman init
command, you may now style your websites with Sass, code in CoffeeScript, and, as you make changes, instantly see the updates reflected in the browser. But we’re not done yet! Not even close.
Package Management
Yeoman leverages a powerful package manager for the web, called Bower. What’s a package manager? Well, if you’re still manually downloading, for instance, the Underscore library from underscorejs.org, then you’re doing it wrong. What happens when the library is updated a few months later? Will you manually redownload the library again? Time is money; so let Yeoman do the work for you.
Let’s pull Underscore into our project.
yeoman install underscore
Yeoman will respond to this command by downloading the latest version of the library, and placing it within a new vendor
directory. Now, it’s ready to be used!
<script src="scripts/vendor/underscore/underscore.js"></script>
But, what if we’re not exactly sure what the name of the asset that we require is? In these situations, we can refer to yeoman search
. Without passing any arguments, Yeoman will return a list of every asset that is available to install. Let’s search for the popular normalize.css
project, by Nicolas Gallagher.
Remember: Bower isn’t exclusively for JavaScript-specific assets.
yeoman search normalize
At the time of this writing, two projects should be returned:
- normalize-css git://github.com/necolas/normalize.css.git
- underscore.normalize git://github.com/michael-lawrence/underscore.normalize.git
It looks like normalize-css
is the one we want.
yeoman install normalize-css
Now, import it in the same way that you normally would:
<link rel="stylesheet" href="scripts/vendor/normalize-css/normalize.css">
Alternatively, rename the file to normalize.scss
, and import it into your main.sass
file.
// main.sass @import '../scripts/vendor/normalize-css/normalize'
There’s a variety of other Bower-specific commands that you’ll want to remember:
-
yeoman uninstall jquery
- Uninstall a package. -
yeoman update jquery
- Update library to the latest version. -
yeoman list
- List all currently installed packages.
Testing
If testing is not yet part of your workflow, it should be! What could be better than a robot that automatically verifies your work after each save? Luckily, Yeoman makes it incredibly easy to test your applications. Out of the box, the popular Mocha framework and PhantomJS (headless Webkit) are available, though it’s easily configurable, if you prefer a different tool, like Jasmine. Additionally, it offers the Chai assertion library, which you’ll quickly grow to love.
Open the tests/index.html
file. Toward the bottom, you’ll see a couple sample tests provided. Go ahead and delete those, and create a new test file: spec/person.js
. Here’s a test to get you started.
// test/spec/person.js describe('A Person', function() { it('should have an age above 0', function() { var person = new Person name: 'Jeffrey', age: 27 expect(person.age).to.be.above(0); }); });
Should Interface
If you’d prefer to use Chai’s (an assertion library) should
interface, return to index.html
, and change expect = chai.expect
to should = chai.should()
. Now, you can update your spec, so that it reads:
person.age.should.be.above(0);
Which method you choose is entirely up to you. There is no correct choice; only preferences.
To run this test, return to the Terminal, and type:
yeoman test
As expected, the test should fail with the message: “Can’t find variable: Person.” It’s a failing test, but, more importantly, it works - we’re testing! Because Yeoman leverages the excellent PhantomJS tool (headless Webkit), these tests can even be run without the browser.
CoffeeScript Tests
If you prefer to write your tests in CoffeeScript, you’ll need to make a couple tweaks to your gruntfile.js
. Begin by adding a new compile
object to the compass
task. Within this object, specify the files that should be watched. In this case, we’re instructing Grunt to compile all CoffeeScript files within test/spec/coffee
.
// Coffee to JS compilation coffee: { dist: { src: 'app/scripts/**/*.coffee', dest: 'app/scripts' }, compile: { files: { "test/spec/": "test/spec/coffee/*.coffee" } } }
The final step is to tell Grunt to keep an eye on that directory. When a file is saved, it should be recompiled, accordingly.
Find the watch
task, and update the coffee
object, like so:
coffee: { files: ['<config:coffee.dist.src>', 'test/spec/coffee/*.coffee'], tasks: 'coffee reload' }
Above, we’re simply adding a new path to the files
array. This way, Grunt knows that it needs to watch the test/spec/coffee
directory as well for changes, and run the coffee
and reload
tasks, accordingly.
Putting It All Together
To illustrate a few more of Yeoman’s abilities, let’s take this new learning, and apply it to a simple project from scratch. Our goal is to display the latest tweets about Yeoman on the page, and include the tweeter’s avatar, and a link to the original tweet. Let’s get started.
We begin by rapidly creating a new application with Yeoman.
mkdir tweets && cd tweets yeoman init quickstart
Next, we boot up the server and begin watching our Sass and CoffeeScript files for changes. If you’re working along, be sure to place your browser and editor side by side for the best workflow.
yeoman server
Feel free to remove the boilerplate HTML that Yeoman provides as an example. Next, we’ll start writing the necessary code to fetch the tweets. Within the scripts/
directory, create a new coffee/tweets.coffee
file, and reference the compiled version of this file within index.html
.
<link rel="stylesheet" href="scripts/tweets.js">
Next, we’ll fetch the the desired tweets using Twitter’s easy-to-use Search API. To fetch a JSON file, containing these tweets, we can use the following URL:
http://search.twitter.com/search.json?q=yeoman.io
However, because we’ll be fetching this data, using $.getJSON
, we’ll need to specify a callback
parameter, so that we trigger Twitter’s JSONP format.
Refer to Twitter’s API for more search options.
Let’s create the class.
App = App or {} class App.TweetsCollection constructor: (query = 'yeoman.io', apiUrl = 'http://search.twitter.com/search.json') -> @query = query @apiUrl = apiUrl fetch: -> $.getJSON "#{@apiUrl}?q=#{@query}&callback=?"
Note that we’re using dependency injection (from the constructor) to make the process of testing this code (beyond the scope of this tutorial) considerably easier.
If you’d like to try it out, within your browser’s console, run:
var tweets = new App.TweetsCollection tweets.fetch().done(function(data) { console.log(data.results); });
The console should now display a list of tweets, which reference “yeoman.io.”
Now that we’ve managed to fetch the tweets, we next need to prepare the HTML to display them. While it’s recommended that you use a proper templating engine, such as Handlebars or Underscore’s implementation, for the purposes of this tutorial, we’ll keep it simple. Luckily, CoffeeScript’s block strings and interpolation features makes the process of embedding HTML as elegant as possible.
class App.TweetsView el: $('<ul>') constructor: (tweets) -> @tweets = tweets render: -> $.each @tweets, (index, tweet) => # Try to use a templating engine instead. @el.append """ <li> <img src='#{tweet.profile_image_url}' alt='#{tweet.from_user}'> #{tweet.text} </li> """ @
Note: when you’re ready to use a dedicated templating engine, don’t forget to install it with Yeoman and, behind the scenes, Bower:
yeoman install handlebars
.
This code is fairly simple. When instantiated, it’ll expect an array of the tweets (which we already know how to fetch). When its render()
method is triggered, it will cycle through that array of tweets, and, for each one, append a list item with the necessary data to an unordered list (@el
). That’s it!
If you’re curious about the
=>
sign (instead of->
), that’s what we refer to as a fat arrow in CoffeeScript. It ensures that, within the anonymous function,this
will still refer to theTweetsView
object, instead of the singletweet
.
Now that our code is in place, let’s get the ball rolling! Back to the index.html
file, add a new app.js
reference.
<script src="scripts/vendor/jquery.min.js"></script> <script src="scripts/tweets.js"></script> <script src="scripts/app.js"></script>
Within scripts/coffee/app.coffee
, add:
tweets = new App.TweetsCollection tweets.fetch().done (data) -> tweetsView = new App.TweetsView(data.results).render() $(document.body).html tweetsView.el
Upon saving this code, thanks to Yeoman, watch the browser instantly refresh to display the latest tweets about Yeoman!
You might be wondering where that done
method is coming from. This is necessary because, behind the scenes, when the fetch()
method is called on App.TweetsCollection
, an AJAX request is being made. As such, a “promise” is being returned.
Think of a promise as jQuery promising to notify you when an asynchronous operation has completed. When this async request is “done,” then execute this callback function.
Admittedly, this was a fairly simple project, but Yeoman has significantly improved our workflow.
The final step is to build the project, in order to optimize our assets and images (if applicable) as much as possible.
yeoman build
This command will instruct Yeoman to run all necessary tasks, and ultimately produce a new dist
directory that should be pushed to your server for production. All files will be compressed and optimized.
Once the operation completes, preview it by running:
yeoman server:dist
View the source, and notice how the assets have been compressed! But we can do better. At this point, the scripts and stylesheets (not applicable in our project) haven’t been concatenated. Let’s fix that with Yeoman!
Return to your index.html
file, and wrap the script
references with an HTML comment, which instructs Yeoman to concatenate and minify the contained files.
<!-- build:js scripts/scripts.js --> <script src="scripts/vendor/jquery.min.js"></script> <script src="scripts/tweets.js"></script> <script src="scripts/app.js"></script> <!-- endbuild -->
This translates to: when building the project, concatenate all of the files within the build:js
comment block, and replace the scripts with a single reference to scripts/scripts.js
, which Yeoman will automatically generate for you. This way, in production, we’re working with only one HTTP request instead of three! This also can be used for your stylesheets, though, if you’re using Sass, it’s unnecessary.
With that change, build and preview the project again.
yeoman build yeoman server:dist
It still works! View the source, and notice that we now only have one script reference.
<script src="scripts/110552aa.scripts.js"></script>
Folks, this is free optimization. No hidden fees. Use it! Your final step would be to push the dist
folder up to your server, and head home for the day!
Closing Thoughts
Yeoman couldn’t have come at a better time.
Perhaps the greatest thing about Yeoman is that it’s open. While some similar tools cost money, Yeoman is open source, which means that you - yes you - can fork it, and help improve it!
As the web moves more and more toward client-side-centric applications, Yeoman couldn’t have come at a better time. So, forget the preparation and configuration; let’s start building things.
To stay up to date on the latest Yeoman news, or to make suggestions and feature requests, feel free to follow @yeoman on Twitter, and subscribe to its Google group.
Comments