Javascript first-class citizenery

32
[email protected] Fast, modular code with RequireJS & node.js JAVASCRIPT & 1 ST CLASS CITIZENRY

description

Javascript and first-class citizenry: require.js & node.jsJavascript on web pages is ubiquitous and its problems are legendary. Javascript, seen as a second-class code citizen, is usually hacked together even by seasoned developers. New libraries (jQuery, prototype, backbone, knockout, underscore) and runtime tools (firebug, jasmine) look like they solve many problems - and they do. But they still leave poorly written code as just that. One key problem is that all javascript code lives globally and this results in poorly managed, tested and delivered code.In this session, I will illustrate that we can treat javascript as a first-class citizen using with require.js and node.js: it can be modular, encapsulated and easily unit tested and added to continuous integration cycle. The dependencies between javascript modules can also be managed and packaged just like in C# and Java. In the end, we can resolve many javascript difficulties at compile time rather than waiting until runtime.

Transcript of Javascript first-class citizenery

Page 1: Javascript first-class citizenery

[email protected]

Fast, modular code with RequireJS & node.js

JAVASCRIPT & 1ST CLASS CITIZENRY

Page 2: Javascript first-class citizenery

Size and complexity

As JavaScript applications grow in size and complexity we need a real tool to handle dependencies and keep our code modular. RequireJS give you an easy way to define dependencies on your module and it'll make sure it’s loaded before your code runs. It also has a nice build system which can be used to compress and obfuscate your code using a closure compiler.

Adapted from: http://www.meetup.com/jQuery-Boston/events/16191792/

Page 3: Javascript first-class citizenery

Agenda

• Global Pollution• Dependencies &

Modularisation• Building and

Testing• Optimisation

Page 4: Javascript first-class citizenery

In Javascript, global functions are not a good strategy. They pollute. So, too do well known namespaces – but we’ll live with that. Modules provide a way forward against these problems.

Global Pollution

Page 5: Javascript first-class citizenery

Badness 1: Global functions

<body><button onclick="addMovie()">add

Movie</button><script type="text/javascript”> function addMovie() {

$.observable( movies ).insert ( this.name); } </script></body>

Page 6: Javascript first-class citizenery

Badness 2: Well-known namespaces<html><head>

<script src=http://code.jquery.com/jquery.js …<script src="jsrender.js" type="text/javascript"></script>

</head><body>

<div id="movieList"><b>Our Movies</b><br /></div>

<script type="text/javascript”>$.templates({ movieTemplate: "#movieList” })

$.template.moveTemplate.render( )</script>

</body></html>

Page 7: Javascript first-class citizenery

Template Pattern

(function($) { // nothing outside here can call this function function sumWithInterest( el, interset){

return $(el).val() * interest; } $.extend($.fn , sumWithInterest) // except …}(jQuery));

Page 8: Javascript first-class citizenery

RequireJs (module) pattern

define([“jquery”], function($) { function sumWithInterest( el, interest){

return $(el) .val() * interest; } return sumWithInterest// object or function });

Page 9: Javascript first-class citizenery

Let’s take that all a little bit more slowly.

Dependencies & Modularisation

Page 10: Javascript first-class citizenery

Each module ecapsulates

define(“main”, function(){

//all code in here // is scoped })

require(“main”)

Create Consume

Page 11: Javascript first-class citizenery

Access in is via a return only

define(“main”, function(){ return {

init: function(){…}

} })

require(“main”).init()

Create Consume

Page 12: Javascript first-class citizenery

Dependencies are explicit

define(“main”, [‘log’, ‘jquery’], function( log, $ ){ log.debug( $(‘body’).text() ) })

Page 13: Javascript first-class citizenery

Module loading can be ordered

define(“main”, [‘log’, ‘order!jquery’, ‘order!jsrender’], function( log, $ ){

$.template(‘body’).render()log.debug( $(‘body’).text() )

})

Page 14: Javascript first-class citizenery

Building & Testing

Page 15: Javascript first-class citizenery

Build command line

• Rake• Gradle• Psake• Msbuild• Ant• Bat• sh

Page 16: Javascript first-class citizenery

Test-first via jasmine

Units• Views• Events

Acceptance• Full loading

Page 17: Javascript first-class citizenery

Test html

describe("Html views", function() { var data = { type: "small", ordered: "1 min ago" } it("has a line item", function() { _requires(["text!coffee/views/_item.html", 'jsrender'], function( text ){ $( "#test" ).html( $.templates( text ).render( data ) ); expect($("li", "#test" ).size()).toEqual(1); }); }

<li> <i>{{:type}}</i> (<abbr class="date”{{:ordered}}</abbr>)</li>

Code template

Test

Page 18: Javascript first-class citizenery

Test Events

it("should register add order handler”, function() {

_requires([“coffee/loader”])); spyOn(_, 'order').andCallThrough() _.orderHandler( $('#test'), function(){ return {} ) $('#test').click()

expect(_.order).toHaveBeenCalled() });

Page 19: Javascript first-class citizenery

Test that it actually works!

it("should be able to add new order", function() { _requires(["coffee/main"])); val = $('li', ’#orders').size(); $('button.order', ’#orders').click() expect($('li', ’#orders').size()).toEqual(val++); });

Page 20: Javascript first-class citizenery

Build and get runtime errors

$ rake buildnode lib/r.js -o app.build.js

Tracing dependencies for: coffee/main

Error: Error evaluating module "undefined" at location "/Users/todd/Documents/src/coffee/build/scripts/coffee/main.js":Unexpected token: punc (,) (line: 4, col: 16, pos: 152)

Page 21: Javascript first-class citizenery

Throw away the debugger

no break points or step through

… no really, I’m serious

Page 22: Javascript first-class citizenery

We still need to package code ready for serving. Why wait until run?

Optimisation

Page 23: Javascript first-class citizenery

Script-tag vomit

In development, it’s good Production, it’s not

Page 24: Javascript first-class citizenery

Pretty clean

Page 25: Javascript first-class citizenery

Create bundles & linted

Page 26: Javascript first-class citizenery

Script and CSS compressed

Page 27: Javascript first-class citizenery

Ready for deployment

Page 28: Javascript first-class citizenery

• File paths between development and production

• CSS url paths• Text (html, json) loading without

server• Creating a localhost server for text• Hassle for smaller projects

Gotchas!

Page 29: Javascript first-class citizenery

• Different AMD implementations• Plugins (page load, internationalisation)• Lazy and dynamic loading• Wrapping non-AMD code• Coffeescript• Data-main loading• Build runners and CI integration

Follow-up areas

Page 30: Javascript first-class citizenery

Finally, what does this mean?

• We can unit test web-based application at the GUI layer

• We CAN reduce the number of expensive, system tests (eg selenium)

• Modularity and testing are likely to allow smaller pieces of work and potentially increased flow

• All this (coupled with REST) means interesting times!

Page 31: Javascript first-class citizenery

Useful refs (and referred to materials)

• http://www.angrycoding.com/2011/09/managing-dependencies-with-requirejs.html

• http://patrickmcelhaney.com/AMD-Talk/#microjs• http://projects.haykranen.nl/amsterdamjs• http://www.slideshare.net/JulienZee/parisjs-10-requirejs-9111799• http://www.slideshare.net/tim_doherty/requirejs-8858167 • http://www.slideshare.net/timoxley/testable-client-

sidemvcappsinjavascript• http://www.slideshare.net/sioked/requirejs

Page 32: Javascript first-class citizenery

Good luck

Todd Brackley

goneopen.comtwitter: toddbNZ

code: github.com/toddb/javascript-amd