Upgrade to Pro — share decks privately, control downloads, hide ads and more …

DRYing Out Your Client-Side Apps

Garann Means
February 12, 2012

DRYing Out Your Client-Side Apps

There's plenty of cool stuff Node offers purely in terms of server-side architectures, but it also offers a way to solve a problem we've been wrestling with since client-side applications became a big deal: writing everything twice. Rather than having the templates that produce markup exist in one backend language and in JavaScript, you can reuse them. Instead of validating in JavaScript on the client for the user's convenience and then again in some other language on the server for security, you can share a validation module that can be used in both scenarios. And so on. We'll look at some of the ways to stop repeating ourselves in Node apps and focus on getting the most out of existing client-side code.

Garann Means

February 12, 2012
Tweet

More Decks by Garann Means

Other Decks in Technology

Transcript

  1. hello! ’ Garann (like Karen with a G) Means ’

    Austin All-Girl Hack Night + Girl Develop It Austin ’ Node for Front-End Developers (O’Reilly) ’ [email protected] or @garannm Sunday, February 12, 12
  2. and why? ’ fast! ’ evented! ’ super easy to

    set up! ’ awesome dev community! ’ reusable application code! Sunday, February 12, 12
  3. maybe a little trickier ’ fast! ’ evented! ’ super

    easy to set up! ’ awesome dev community! ’ reusable application code! Sunday, February 12, 12
  4. we never needed that before ’ client-side strictly presentational ’

    DHTML ’ Ajax ’ explosion of JS libraries ’ client-side apps Sunday, February 12, 12
  5. attempting reuse with a traditional server ’ server-side interpreters ’

    compiled versions of logic for client ’ limited vocabulary that can be truly shared Sunday, February 12, 12
  6. things we can reuse ’ models ’ views/templates ’ route

    patterns ’ external or third-party utilities Sunday, February 12, 12
  7. caveats ’ essentially one way to import in node ’

    modules have to check their context ’ may need to be both synchronous and asynchronous ’ user interaction has to be abstracted ’ no global scope on the server Sunday, February 12, 12
  8. models ’ easy-peasy! ’ sensible and simple to structure data

    same way ’ validation and rendering signatures can be the same ’ (even if functionality differs) Sunday, February 12, 12
  9. models function Model( config ) { var that = {

    thing: config.thing, stuff: config.stuff || [] }; that.validate = function() { if ( that.stuff.length ) return true; } return that; } Sunday, February 12, 12
  10. server socket.on( “modelUpdated”, function( data ) { var m =

    new Model( data ); if ( !m.validate() ) { socket.emit( “badData”, { message: “onoez” } ); } }); Sunday, February 12, 12
  11. client btn.addEventListener( “click”, function() { var m = new Model({

    thing: txtField.value, stuff: convenientlyDefinedArray }); if ( m.validate() ) { socket.emit ( “modelUpdated”, m ); } else { alert( “Oops - not enough stuff!” ); } }); Sunday, February 12, 12
  12. templates ’ not all JS templates work client/server ’ all

    can be *made* to work client/server ’ used differently, loaded differently Sunday, February 12, 12
  13. server ’ maximize size ’ minimize requests ’ child templates

    as dependencies ’ no need to cache ’ response has to wait for loading, compositing, rendering Sunday, February 12, 12
  14. client ’ minimize size ’ minimize DOM interaction ’ swap

    out small bits of innerHTML ’ caching probably important ’ may be re-rendering constantly Sunday, February 12, 12
  15. routes ’ generic functions or publish/subscribe ’ probably need to

    abstract input and output ’ RPC/RMI may substitute Sunday, February 12, 12
  16. routes namespace.save = function( input, output ) { output.render (

    “save”, input ); } Sunday, February 12, 12
  17. client btn.addEventListener( “click”, function() { var formData = serializeData( “#myForm”

    ); routes.save( formData, myRenderer ); }); var myRenderer = function() { this.render = function( tmplName, data ) { document.querySelector( “#page” ).innerHTML = tmplEngine.template( tmplName, data ); }; }; Sunday, February 12, 12
  18. utilities ’ client-side utils may rely on a global namespace

    ’ node modules probably expect exports ’ like templates, can be *made* to work bi-directionally Sunday, February 12, 12
  19. generally.. ’ files live on client ’ pretend you never

    heard of global scope ’ everything decoupled from DOM* ’ pass reference to anything with state Sunday, February 12, 12
  20. server-side DOM ’ existing tools to create DOM in V8

    ’ slow ’ server-side DOM rarely needed ’ for entire apps, almost never ’ have to replace or trigger user interactions Sunday, February 12, 12
  21. perfect candidates ’ testing ’ spidering ’ scraping ’ anything

    that requires parsing markup Sunday, February 12, 12
  22. WTF candidates ’ games ’ visual editors ’ anything where

    markup is merely the final representation ’ anything highly interactive Sunday, February 12, 12
  23. where it gets used ’ templates ’ build up DOM

    with reference to elements (vs. string) ’ events “belong” to elements ’ existing client-side apps ’ quick n’ dirty Sunday, February 12, 12
  24. require.js and r.js ’ node’s require is subtly different ’

    formal dependency management solves namespace issue ’ kind of ’ text! prefix for loading unexecuted code Sunday, February 12, 12
  25. server var requirejs = require( “requirejs” ); requirejs.config( { nodeRequire:

    require } ); requirejs( [“thing1”, “thing2”], function( t1, t2 ) { // whatever.. }); requirejs( [“text!public/template.html”], function( tmpl ) { // whatever (rendering version).. }); Sunday, February 12, 12
  26. client <script src=”require.js”></script> <script> requirejs( [“thing1”, “thing2”], function( t1, t2

    ) { // whatever.. }); requirejs( [“text!public/template.html”], function( tmpl ) { // whatever (rendering version).. }); </script> Sunday, February 12, 12
  27. exports || namespace ’ everything in an IIFE ’ pass

    in scope ’ requires hard-coding client namespace Sunday, February 12, 12
  28. exports || namespace (function( ns ) { ns.thing = function()

    { // whatever.. }; return ns; }( ( typeof process !== "undefined" && process.title === "node" ) ? exports : clientNamespace )); Sunday, February 12, 12
  29. RPC/rmi ’ write code once to run in two places

    ’ both, not either ’ pass control with callbacks (which node needs anyway) Sunday, February 12, 12
  30. server var dnode = require( “dnode” ); dnode( function( client,

    connection ) { this.biznessLogic = function() { // whatever.. } }).listen( 1337 ); Sunday, February 12, 12
  31. client var dnode = require( “dnode” ); dnode.connect( function( remote

    ) { btn.addEventListener( “click”, function() { remote.biznessLogic(); }); }); Sunday, February 12, 12
  32. the future ’ assembling this stuff by hand is silly

    ’ reuse should be the default ’ room for many frameworks, many methodologies Sunday, February 12, 12
  33. how would that look? ’ build step required? ’ framework

    creates server ’ server generates client-side assets from server code ’ controllers wrap RPC/RMI calls Sunday, February 12, 12
  34. full-stack solutions ’ batman.js ’ derby.js ’ flatiron.js ’ thorax.js

    ’ drumkit.js ’ zomg: http://toolbox.no.de/categories/Frameworks Sunday, February 12, 12
  35. changing everything ’ trying to do this since early 2000s,

    at least ’ and it’s sucked ’ browsers behaving more like servers ’ sockets, storage, I/O ’ and servers behaving more like browsers ’ \m/, JavaScript ,\m/ Sunday, February 12, 12
  36. why the proliferation? ’ everyone coming to the same conclusion

    at once ’ different needs, but not really ’ different preferences ’ which fits your needs? Sunday, February 12, 12
  37. lots of ways to share code ’ which pieces do

    you have? ’ which are crucial and which are “glue”? ’ e.g. templates may be unrelated to biz logic ’ are there constraints to how they’re handled? ’ e.g. maybe you can’t jump to a route/state by URL Sunday, February 12, 12
  38. bottom line: Don’t repeat yourself ’ no reason to run

    logic on a different side than it’s used ’ no reason to duplicate code Sunday, February 12, 12
  39. — image credits — ’ http://www.flickr.com/photos/breakfastcore/2285501111/ ’ http://www.flickr.com/photos/bondidwhat/6114903414/ ’ http://www.flickr.com/photos/rchappo2002/2694370462/

    ’ http://www.flickr.com/photos/21233184@N02/4188316428/ ’ http://www.flickr.com/photos/blackcountrymuseums/5218004706/ ’ http://www.flickr.com/photos/catechism/3001280935/ ’ http://www.flickr.com/photos/murrus/2344711309/ ’ http://www.flickr.com/photos/carbonated/2105145079/ Sunday, February 12, 12