Patterns of Large-Scale JavaScript Applications

Patterns of Large-Scale JavaScript Applications

Talk held at front/> in Zürich

6c51c14716e24bc1f1a3fb5ad234e773?s=128

Kim Joar Bekkelund

August 29, 2013
Tweet

Transcript

  1. 8.
  2. 11.

    $

  3. 19.

    jQuery(document).ready(function() { $(".user form").submit(function(e) { e.preventDefault(); $.ajax({ // ... success:

    function(data) { var name = data.name || "Unknown"; $(".user .info").append("<h1>" + name + "</h1>"); } }) }); }); Inspired by https://speakerdeck.com/bkeepers/the-plight-of-pinocchio and @searls
  4. 20.

    jQuery(document).ready(function() { $(".user form").submit(function(e) { e.preventDefault(); $.ajax({ // ... success:

    function(data) { var name = data.name || "Unknown"; $(".user .info").append("<h1>" + name + "</h1>"); } }) }); }); Page event User event Hitting the server Nested callback Parsing response Templating Callback hell Inspired by https://speakerdeck.com/bkeepers/the-plight-of-pinocchio and @searls
  5. 25.
  6. 26.
  7. 31.

    we need to unlearn our DOM-centric approach The DOM is

    Inherently global and stateful hard to test and maintain
  8. 33.

    we need to unlearn how we write JavaScript No more

    5k lines of code in a single file Watch out for global variables Learn from the backend
  9. 35.

    we need to unlearn how we use jQuery “Our experience

    is that, when code is difficult to test, the most likely cause is that our design needs improving.” – Growing Object-Oriented Software, Guided by Tests
  10. 37.

    – Justin Meyer “The secret to building large apps is

    never build large apps. Break up your applications into small pieces. Then, assemble those testable, bite-sized pieces into your big application.” http://bitovi.com/blog/2010/11/organizing-a-jquery-application.html
  11. 38.
  12. 41.

    MVC

  13. 46.

    MV*

  14. 49.

    MVVM exposes model so it can be easily managed and

    consumed Bi-directional binding
  15. 50.

    “I hereby declare AngularJS to be MVW framework – Model-View-Whatever.

    Where Whatever stands for ‘whatever works for you’.” – Igor Minar https://plus.google.com/app/basic/stream/z13vitkjpya0zxcee23ztzmqcor3fzen2
  16. 54.
  17. 56.

    “Actually how you partition your code has a lot to

    do with how you organize your project source code and how much easier it is to maintain as your project grows.” – Jim Lavin http://codingsmackdown.tv/blog/2013/04/19/angularjs-modules-for-great-justice/
  18. 62.
  19. 63.
  20. 64.
  21. 65.
  22. 70.
  23. 73.

    “A system is easier to change if its objects are

    context-independent; that is, if each object has no built-in knowledge about the system in which it executes.” – Growing Object-Oriented Software, Guided by Tests
  24. 76.

    Module rules Never access the DOM outside the module Don’t

    create global variables http://www.slideshare.net/nzakas/scalable-javascript-application-architecture-2012
  25. 77.

    Module rules Never access the DOM outside the module Don’t

    create global variables Don’t access global, non-native objects http://www.slideshare.net/nzakas/scalable-javascript-application-architecture-2012
  26. 78.

    Keeping this in mind should get you a long way

    http://www.flickr.com/photos/lrargerich/4999906554/
  27. 80.

    We depend on other code dependencies We depend on other

    files We depend on Third-party libraries
  28. 83.

    We usually depend far too much on globals JavaScript makes

    this really easy We depend on other code
  29. 88.
  30. 89.

    var downloadEmojis = function(ajax, $el) { ajax.get('https://api.github.com', function(data) { $el.html(data.emojis_url);

    }); }; $('#my-button').click(function() { downloadEmojis($, $('#res')); }); Now we can control the dependencies
  31. 90.
  32. 91.

    var UserView = function(el, user) { this.el = el; this.user

    = user; }; // let's just create a super simple user object var user = { image: 'http://example.com/image.png' }; // initialize with the element the view owns and a user var view = new UserView($('.user'), user);
  33. 92.

    var UserView = function(el, user) { this.el = el; this.user

    = user; }; UserView.prototype.showImage = function() { this.el.append('<img src=' + this.user.image + '>'); }; // let's just create a super simple user object var user = { image: 'http://example.com/image.png' }; // initialize with the element the view owns and a user var view = new UserView($('.user'), user);
  34. 93.

    var UserView = function(el, user) { this.el = el; this.user

    = user; }; UserView.prototype.showImage = function() { this.el.append('<img src=' + this.user.image + '>'); }; // let's just create a super simple user object var user = { image: 'http://example.com/image.png' }; // initialize with the element the view owns and a user var view = new UserView($('.user'), user); // ... and now we can do stuff which changes the DOM view.showImage();
  35. 94.

    var UserView = function(el, user) { this.el = el; this.user

    = user; }; UserView.prototype.showImage = function() { this.el.append('<img src=' + this.user.image + '>'); }; // let's just create a super simple user object var user = { image: 'http://example.com/image.png' }; // initialize with the element the view owns and a user var view = new UserView($('.user'), user); // ... and now we can do stuff which changes the DOM view.showImage(); $(‘<div></div>’) In tests
  36. 98.

    var MyController = function($scope, $http) { $http.get('https://api.github.com') .success(function(data) { $scope.emojis_url

    = data.emojis_url; }); } the ordering of these does not matter An example using AngularJS
  37. 99.

    var MyController = function($http, $scope) { $http.get('https://api.github.com') .success(function(data) { $scope.emojis_url

    = data.emojis_url; }); } the ordering of these does not matter An example using AngularJS
  38. 101.

    // Create an AngularJS module with no dependencies var conf

    = angular.module('conf', []); // Register a function conf.factory('awesome', function() { return 'awesome conference'; });
  39. 102.

    // Create an AngularJS module with no dependencies var conf

    = angular.module('conf', []); // Register a function conf.factory('awesome', function() { return 'awesome conference'; }); // Get the injector (happens behind the scenes in AngularJS apps) var injector = angular.injector(['conf', 'ng']);
  40. 103.

    // Create an AngularJS module with no dependencies var conf

    = angular.module('conf', []); // Register a function conf.factory('awesome', function() { return 'awesome conference'; }); // Get the injector (happens behind the scenes in AngularJS apps) var injector = angular.injector(['conf', 'ng']);
  41. 104.

    // Create an AngularJS module with no dependencies var conf

    = angular.module('conf', []); // Register a function conf.factory('awesome', function() { return 'awesome conference'; }); // Get the injector (happens behind the scenes in AngularJS apps) var injector = angular.injector(['conf', 'ng']); // Call a function with dependency injection injector.invoke(function(awesome) { console.log('awesome() == ' + awesome); });
  42. 105.

    // Create an AngularJS module with no dependencies var conf

    = angular.module('conf', []); // Register a function conf.factory('awesome', function() { return 'awesome conference'; }); // Get the injector (happens behind the scenes in AngularJS apps) var injector = angular.injector(['conf', 'ng']); // Call a function with dependency injection injector.invoke(function(awesome) { console.log('awesome() == ' + awesome); });
  43. 109.

    {{#if isExpanded}} <div class='body'>{{body}}</div> <button {{action contract}}>Contract</button> {{else}} <button {{action

    expand}}>Show More...</button> {{/if}} App.PostController = Ember.ObjectController.extend({ body: 'body', isExpanded: false, expand: function() { this.set('isExpanded', true); }, contract: function() { this.set('isExpanded', false); } });
  44. 110.

    {{#if isExpanded}} <div class='body'>{{body}}</div> <button {{action contract}}>Contract</button> {{else}} <button {{action

    expand}}>Show More...</button> {{/if}} App.PostController = Ember.ObjectController.extend({ body: 'body', isExpanded: false, expand: function() { this.set('isExpanded', true); }, contract: function() { this.set('isExpanded', false); } }); no jquery!
  45. 111.

    dependencies We depend on other code We depend on other

    files We depend on Third-party libraries
  46. 118.

    CommonJS AMD ECMAScript 6 var $ = require('jquery'); exports.myFn =

    function() {}; define(['jquery'] , function($) { return function() {}; });
  47. 119.

    CommonJS AMD ECMAScript 6 var $ = require('jquery'); exports.myFn =

    function() {}; define(['jquery'] , function($) { return function() {}; }); import $ from 'jquery'; var myFn = function() {} export { myFn };
  48. 120.

    CommonJS AMD ECMAScript 6 var $ = require('jquery'); exports.myFn =

    function() {}; define(['jquery'] , function($) { return function() {}; }); import $ from 'jquery'; var myFn = function() {} export { myFn }; works better in current browsers
  49. 121.

    AMD

  50. 123.

    <script data-main="scripts/main" src="require.js"></script> // scripts/main.js requirejs.config({ paths: { 'jquery': 'lib/jquery',

    'underscore': 'lib/underscore', 'backbone': 'lib/backbone', } }); define(['app', 'jquery'], function(App, $) { var app = new App({ el: $('body') }); app.start(); });
  51. 125.

    dependencies We depend on other code We depend on other

    files We depend on Third-party libraries
  52. 130.
  53. 134.
  54. 135.