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

Patterns of Large-Scale JavaScript Applications

Patterns of Large-Scale JavaScript Applications

Talk held at front/> in Zürich

Kim Joar Bekkelund

August 29, 2013
Tweet

More Decks by Kim Joar Bekkelund

Other Decks in Programming

Transcript

  1. $

  2. 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
  3. 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
  4. we need to unlearn our DOM-centric approach The DOM is

    Inherently global and stateful hard to test and maintain
  5. 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
  6. 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
  7. – 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
  8. MVC

  9. MV*

  10. MVVM exposes model so it can be easily managed and

    consumed Bi-directional binding
  11. “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
  12. “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/
  13. “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
  14. Module rules Never access the DOM outside the module Don’t

    create global variables http://www.slideshare.net/nzakas/scalable-javascript-application-architecture-2012
  15. 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
  16. Keeping this in mind should get you a long way

    http://www.flickr.com/photos/lrargerich/4999906554/
  17. We depend on other code dependencies We depend on other

    files We depend on Third-party libraries
  18. We usually depend far too much on globals JavaScript makes

    this really easy We depend on other code
  19. 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
  20. 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);
  21. 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);
  22. 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();
  23. 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
  24. 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
  25. 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
  26. // Create an AngularJS module with no dependencies var conf

    = angular.module('conf', []); // Register a function conf.factory('awesome', function() { return 'awesome conference'; });
  27. // 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']);
  28. // 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']);
  29. // 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); });
  30. // 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); });
  31. {{#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); } });
  32. {{#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!
  33. dependencies We depend on other code We depend on other

    files We depend on Third-party libraries
  34. CommonJS AMD ECMAScript 6 var $ = require('jquery'); exports.myFn =

    function() {}; define(['jquery'] , function($) { return function() {}; });
  35. CommonJS AMD ECMAScript 6 var $ = require('jquery'); exports.myFn =

    function() {}; define(['jquery'] , function($) { return function() {}; }); import $ from 'jquery'; var myFn = function() {} export { myFn };
  36. 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
  37. AMD

  38. <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(); });
  39. dependencies We depend on other code We depend on other

    files We depend on Third-party libraries