this is not a TEDTALK
not high-performance javascript
ultra high-performance javascript
what is ultra high-performance?
made possible via• fast file loading.• small file sizes.• avoiding DOM bottlenecks.
what high-performance looks like
hint: in general it looks awful
high-performance != maintainabilitythe elements of high-performance are usuallyat odds with best practices in maintainabil...
an approachstart with maintainability, and achieve high-performance by building it in via automatedprocesses.
large-scale JSto write maintainable code, look at the patternsused by other large-scale JS frameworks:
large-scale JS• separate code into modules, each of which  accomplishes a single function.• expose the module through an i...
modularprogramming
module patternmodule consists of 3 parts:
module patternmodule consists of 3 parts:1. function (what it does)
var _transform = function(sel) {        $(sel).toggleClass(robot);}
// wrapped in a self-executing functionvar transformer = function() {        var _transform = function(sel) {             ...
module patternmodule consists of 3 parts:1. function (what it does)2. dependencies (what it needs)
var transformer = function($) {        var _transform = function(sel) {                $(sel).toggleClass(robot);        }...
module patternmodule consists of 3 parts:1. function (what it does)2. dependencies (what it needs)3. interface (what it re...
var transformer = function($) {        var _transform = function(sel) {                 $(sel).toggleClass(robot);        ...
var transformer = function($) {        var _transform = function(sel) {                 $(sel).toggleClass(robot);        ...
var transformer = function($) {        var _transform = function(sel) {                 $(sel).toggleClass(robot);        ...
benefits of modular programming• self contained – includes everything it needs  to accomplish its function.• namespaced – ...
// you can’t do this… yetimport "transformer.js" as transformer;
3rd party loaders
3rd party loaders• LABjs• HeadJS• ControlJS• RequireJS• Load.js• YepNope.js• $script.js
3rd party loaders• LABjs             • LazyLoad• HeadJS            • curl.js• ControlJS         • JsDefer• RequireJS      ...
3rd party loaders• LABjs             • LazyLoad• HeadJS            • curl.js• ControlJS         • JsDefer• RequireJS      ...
I’ll make it easy…
I’ll make it easy…just use RequireJS.
I’ll make it easy…just use RequireJS.• plugin architecture (text, l10n, css, etc).• built in support for has.js.• support ...
// vanilla js modulevar transformer = function($) {          var _transform = function(sel) {                   $(sel).tog...
// AMD module wraps everything in `define`define(function($) {        var _transform = function(sel) {                 $(s...
// dependency array is the first parameter of define// dependencies mapped to parameters in the callbackdefine([          ...
// dependency array is the first parameter of define// dependencies mapped to parameters in the callbackdefine([          ...
// usagerequire([          transformer], function(transformer) {          transformer.transform(.car);});
example website• common.js – code shared by all pages.• home/main.js – code unique to the home  page.
// common.jsdefine([          jquery,          ui/jquery.ui.core,          ui/jquery.ui.widget], function($) {          //...
// common.jsdefine([          jquery,          ui/jquery.ui.core,          ui/jquery.ui.widget], function($) {          //...
// common.jsrequirejs.config({          shim: {                    ui/jquery.ui.core: {                             deps: ...
// home/main.jsdefine([          common,          ui/jquery.ui.dialog], function($) {          $(.modal).dialog();});
// home/main.jsdefine([          common,          ui/jquery.ui.dialog], function($) {          $(.modal).dialog();});// in...
// home/main.jsdefine([          common,          ui/jquery.ui.dialog], function($) {          $(.modal).dialog();});// in...
behind the scenes (phase 1)1. download require.js – 1.2. download home/main.js – 2.3. check dependencies.4. download commo...
behind the scenes (phase 2)1. evaluate jquery, then jquery.ui.core, then   jquery.ui.widget.2. execute the common.js callb...
modular and maintainablebut crappy performance: 7 requests!
make it high-performance
introducing r.jsmodule optimizer.
// build.js({          modules: [                 {                       name: "common"                 },               ...
// run it manually// or as part of automated build processjava -classpath r.js/lib/rhino/js.jar           org.mozilla.java...
// example outputTracing dependencies for: commoncommon.js----------------jquery.jsui/jquery.ui.coreui/jquery.ui.widgetcom...
// example outputTracing dependencies for: home/mainhome/main.js----------------ui/jquery.ui.dialoghome/main.js
// example outputUglifying file: common.jsUglifying file: home/main.js
new behind the scenes (phase 1)1. download require.js – 1.2. download home/main.js (includes   ui/jquery.ui.dialog) – 2.3....
only 3 requests!• only 1 request per page after initial page  load (require.js and common.js are cached  for all pages).• ...
mandatory builds for UI sucks
// build.js({          baseUrl: "js-src/", // input folder          dir: "js/", // output folder          modules: [      ...
// index.html<script src="js/require.js" data-main="<?php echo $_GET[dev] ? js-src/home/main : js/home/main ?>"></script>/...
even better performance with has.jsfeature detection library.
define([          has], function($) {          // add a test          var re = /bdevb/;          has.add(dev,re.test(windo...
define([          has], function($) {          // add a test          var re = /bdevb/;          has.add(dev,re.test(windo...
// build.js({          baseUrl: "js-src/",          dir: "js/",          has: {                     dev: false          },...
// originalif (has(dev)) {          console.log(test);}
// originalif (has(dev)) {          console.log(test);}// after r.js pre-processingif (false) {           console.log(test...
// originalif (has(dev)) {          console.log(test);}// after r.js pre-processingif (false) {           console.log(test...
has.add(ie7-support, true);if (has(ie7-support) {          // some godawful hack to fix something in ie7}
make it ultra high-performance
even better performance with almondintended for single page apps or mobile whererequest latency is much worse than desktop...
only 1 request… ever.• shaves 14k of boilerplate.
1st step to ultra high performanceuse modular programming.• combine with require.js for asynchronous /  parallel loading.•...
anyone not use jquery?“Study shows half of all websites use jQuery”                                  – August, 2012
// example of a jquery plugin used with a moduledefine([          jquery,          jquery.craftyslide], function($) {     ...
// closer look at `craftyslide.js`$.fn.craftyslide = function (options) {         function paginate() {                   ...
problem with jquery pluginsthey’re a black box.• not easily extendable.• not easily testable.
problem with jquery pluginsthey’re a black box.• not easily extendable.• not easily testable.jquery ui set out to solve th...
uiwidgets
oh noes! not jquery uibloated piece of crap (210k omg!)• jquery ui is modular – use just the bits you  need.• ui core + ui...
ui widgetsthe two things plugins suck at, widgets doreally well:• theyre fully extendable.
simple javascript inheritence25 lines of javascript sexiness:• constructors.• object-oriented inheritence.• access to over...
simple javascript inheritence25 lines of javascript sexiness:• constructors.• object-oriented inheritence.• access to over...
// example widget$.widget(ui.transformer, {        options: {                 …        },        _create: function() {    ...
// example widget$.widget(ui.transformer, {        options: {                 …        },        _create: function() {    ...
not-so simple javascript inheritenceeverything from simple javascript inheritence,plus:• namespaces.• public and private m...
ui widgetsthe two things plugins suck at, widgets doreally well:• theyre fully extendable.• theyre tuned for testing.
// if `craftyslide` were a widget$.widget(ui.craftyslide, {           _create: function() {                    …          ...
// adding triggers as hooks for testing$.widget(ui.craftyslide, {         …         _paginate: function(){                ...
// in your unit testfunction beforePaginate() {         // test conditions}function afterPaginate() {         // test cond...
// plugin using `.on()`function manual() {         …         $pagination.on(click, function (e) {                  …      ...
// plugin using `.on()`function manual() {         …         $pagination.on(click, function (e) {                  …      ...
// `._on()` remembers all event bindings_on: function( element, handlers ) {         …         this.bindings = this.bindin...
// `._on()` remembers all event bindings_on: function( element, handlers ) {         …         this.bindings = this.bindin...
// `._on()` remembers all event bindings_on: function( element, handlers ) {         …         this.bindings = this.bindin...
// setup widget$(#slideshow).craftyslide();// run tests…// teardown// calls `.destroy()`// which automatically unbinds all...
high-performance from code re-use
define([          jquery,          ui/jquery.ui.core,          ui/jquery.ui.widget,          ui/jquery.ui.craftyslide], fu...
2nd step to ultra high performanceuse object-oriented widgets as code buildingblocks.• inheritance promotes code re-use, s...
made possible via• fast file loading.• small file sizes.• avoiding DOM bottlenecks.
avoiding DOM bottlenecks
eventdelegation
<ul id="transformers">         <li><a>Bumblebee</a></li>         <li><a>Ratchet</a></li>         <li><a>Ironhide</a></li><...
<ul id="transformers">         <li><a>Bumblebee</a></li>         <li><a>Ratchet</a></li>         <li><a>Ironhide</a></li><...
// event delegation is similar$(#transformers).on(click, a, function() {         // do something});
// event delegation is similar$(#transformers).on(click, a, function() {         // do something});// but allows us to do ...
why does that kick ass?• more performant – less memory, faster to  bind/unbind.• less maintenance – you can add/remove <ul...
how does this work with widgets?it doesnt – widgets pitfall is they are a DOMbottleneck.
// example `lightbox` widget$(#gallery a).lightbox();// widget depends on `this.element`$.widget(ui.lightbox, {         _c...
two workarounds• one for legacy widgets.• better approach for new widgets.
// legacy widgets$(document).on(click, #gallery a, function() {         $(this)                 .lightbox()               ...
// new widgets$.widget(ui.lightbox, {        _create: function() {                  var sel = this.options.selector;      ...
// new widgets$.widget(ui.lightbox, {        _create: function() {                  var sel = this.options.selector;      ...
3rd step to ultra high performancedelegate anything and everything you can.• will add interaction to elements that are  la...
delegation isn’t a cure alldelegation works great when the widgetdoesnt need to know about the user up untilthe user inter...
how we’ve done this previously• document.load – the 80s of the internet.• document.DOMContentLoaded – the new  load event!
domready considered an anti-pattern“the short story is that we dont want towait for DOMContentReady (or worse theload even...
<ul id="transformers">         <li><a>Bumblebee</a></li>         <li><a>Ratchet</a></li>         <li><a>Ironside</a></li><...
oh no you didn’ta problem with our modular approach:• nothing is exposed to the global scope  – you cant use modules from ...
mediator pattern to the rescuea central point of control that modulescommunicate through – instead ofdirectly with each ot...
pubsub
central point of control• publish• subscribe• unsubscribe
it’s so easy• publish = $.trigger• subscribe = $.on• unsubscribe = $.off
// in codevar proxy = $({});window.publish = function() {         proxy.trigger.apply(proxy, arguments);}window.subscribe ...
<ul id="transformers">         <li><a>Bumblebee</a></li>         <li><a>Ratchet</a></li>         <li><a>Ironside</a></li><...
define([          main], function() {          subscribe(load.transformers, function() {                  $(#transformers)...
oh no you didnttwo problems with our modular approach:• nothing is exposed to the global scope  – you cant use modules fro...
<head>// blocking, should be tiny (1k) or inlined!<script src="bootstrap.js"></script>// asynchronous non-blocking<script ...
// bootstrap.js// needs to be some global object// but we can clean it up afterwardsdocument.queue = [];window.publish = f...
<script>         publish(load.transformers);</script>// document.queue = [[load.transformers]]
// main.jsdefine([          jquery], function($) {          var proxy = $({});          window.publish = function() {     ...
window.subscribe = function(event) {       proxy.on.apply(proxy, arguments);});
window.subscribe = function(event) {       proxy.on.apply(proxy, arguments);       $(document.queue).each(function(index) ...
ultra high-performance achieved!
ultra high-performance achieved!1. use modular programming.2. use object-oriented widgets as code   building blocks.3. del...
onesec
about me
about me• I like Land Cruisers.• lived in Costa Rica for 10 years (there is no  excuse for how I speak).• UI dev lead / mo...
questionspreguntas?
Beyond DOMReady: Ultra High-Performance Javascript
Beyond DOMReady: Ultra High-Performance Javascript
Beyond DOMReady: Ultra High-Performance Javascript
Upcoming SlideShare
Loading in …5
×

Beyond DOMReady: Ultra High-Performance Javascript

3,129 views
3,008 views

Published on

Leverage patterns of large-scale JS – such as modules, publish-subscribe and delegation – to achieve extreme performance without sacrificing maintainability.

0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,129
On SlideShare
0
From Embeds
0
Number of Embeds
50
Actions
Shares
0
Downloads
32
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide

Beyond DOMReady: Ultra High-Performance Javascript

  1. 1. this is not a TEDTALK
  2. 2. not high-performance javascript
  3. 3. ultra high-performance javascript
  4. 4. what is ultra high-performance?
  5. 5. made possible via• fast file loading.• small file sizes.• avoiding DOM bottlenecks.
  6. 6. what high-performance looks like
  7. 7. hint: in general it looks awful
  8. 8. high-performance != maintainabilitythe elements of high-performance are usuallyat odds with best practices in maintainability…
  9. 9. an approachstart with maintainability, and achieve high-performance by building it in via automatedprocesses.
  10. 10. large-scale JSto write maintainable code, look at the patternsused by other large-scale JS frameworks:
  11. 11. large-scale JS• separate code into modules, each of which accomplishes a single function.• expose the module through an interface.
  12. 12. modularprogramming
  13. 13. module patternmodule consists of 3 parts:
  14. 14. module patternmodule consists of 3 parts:1. function (what it does)
  15. 15. var _transform = function(sel) { $(sel).toggleClass(robot);}
  16. 16. // wrapped in a self-executing functionvar transformer = function() { var _transform = function(sel) { $(sel).toggleClass(robot); }}();
  17. 17. module patternmodule consists of 3 parts:1. function (what it does)2. dependencies (what it needs)
  18. 18. var transformer = function($) { var _transform = function(sel) { $(sel).toggleClass(robot); }}(jQuery);
  19. 19. module patternmodule consists of 3 parts:1. function (what it does)2. dependencies (what it needs)3. interface (what it returns)
  20. 20. var transformer = function($) { var _transform = function(sel) { $(sel).toggleClass(robot); } // sets what `transformer` is equal to return = { transform: _transform }}(jQuery);
  21. 21. var transformer = function($) { var _transform = function(sel) { $(sel).toggleClass(robot); } // sets what `transformer` is equal to return = { transform: _transform }}(jQuery);// usagetransformer.transform(.car);
  22. 22. var transformer = function($) { var _transform = function(sel) { $(sel).toggleClass(robot); } // sets what `transformer` is equal to return = { transform: _transform }}(jQuery);// usagetransformer.transform(.car);// result<div class="car robot" />
  23. 23. benefits of modular programming• self contained – includes everything it needs to accomplish its function.• namespaced – doesnt dirty the global scope.
  24. 24. // you can’t do this… yetimport "transformer.js" as transformer;
  25. 25. 3rd party loaders
  26. 26. 3rd party loaders• LABjs• HeadJS• ControlJS• RequireJS• Load.js• YepNope.js• $script.js
  27. 27. 3rd party loaders• LABjs • LazyLoad• HeadJS • curl.js• ControlJS • JsDefer• RequireJS • jquery.defer.js• Load.js • BravoJS• YepNope.js • JSLoad• $script.js • StealJS
  28. 28. 3rd party loaders• LABjs • LazyLoad• HeadJS • curl.js• ControlJS • JsDefer• RequireJS • jquery.defer.js• Load.js • BravoJS• YepNope.js • JSLoad• $script.js • StealJS …and more
  29. 29. I’ll make it easy…
  30. 30. I’ll make it easy…just use RequireJS.
  31. 31. I’ll make it easy…just use RequireJS.• plugin architecture (text, l10n, css, etc).• built in support for has.js.• support for r.js.• James Burke knows his shit.• author of the AMD standard.
  32. 32. // vanilla js modulevar transformer = function($) { var _transform = function(sel) { $(sel).toggleClass(robot); } return = { transform: _transform }}(jQuery);
  33. 33. // AMD module wraps everything in `define`define(function($) { var _transform = function(sel) { $(sel).toggleClass(robot); } return = { transform: _transform }}(jQuery));
  34. 34. // dependency array is the first parameter of define// dependencies mapped to parameters in the callbackdefine([ jquery], function($) { var _transform = function(sel) { $(sel).toggleClass(robot); } return = { transform: _transform }});
  35. 35. // dependency array is the first parameter of define// dependencies mapped to parameters in the callbackdefine([ jquery, underscore], function($, _) { var _transform = function(sel) { $(sel).toggleClass(robot); } return = { transform: _transform }});
  36. 36. // usagerequire([ transformer], function(transformer) { transformer.transform(.car);});
  37. 37. example website• common.js – code shared by all pages.• home/main.js – code unique to the home page.
  38. 38. // common.jsdefine([ jquery, ui/jquery.ui.core, ui/jquery.ui.widget], function($) { // setup code for all pages});
  39. 39. // common.jsdefine([ jquery, ui/jquery.ui.core, ui/jquery.ui.widget], function($) { // setup code for all pages});// but `jquery.ui.core` and `jquery.ui.widget`// aren’t modules!
  40. 40. // common.jsrequirejs.config({ shim: { ui/jquery.ui.core: { deps: [jquery] }, ui/jquery.ui.widget: { deps: [ui/jquery.ui.core] } }});define([ jquery, ui/jquery.ui.core, ui/jquery.ui.widget], function($) {
  41. 41. // home/main.jsdefine([ common, ui/jquery.ui.dialog], function($) { $(.modal).dialog();});
  42. 42. // home/main.jsdefine([ common, ui/jquery.ui.dialog], function($) { $(.modal).dialog();});// index.html<script src="require.js" data-main="home/main"></script>
  43. 43. // home/main.jsdefine([ common, ui/jquery.ui.dialog], function($) { $(.modal).dialog();});// index.html<script src="require.js" data-main="<?php echo $template?>/main"></script>
  44. 44. behind the scenes (phase 1)1. download require.js – 1.2. download home/main.js – 2.3. check dependencies.4. download common.js, ui/jquery.ui.dialog – 4.5. check dependencies.6. download jquery, ui/jquery.ui.core, ui/jquery.ui.widget – 7.7. check dependencies.
  45. 45. behind the scenes (phase 2)1. evaluate jquery, then jquery.ui.core, then jquery.ui.widget.2. execute the common.js callback.3. evaluate jquery.ui.dialog.4. execute the home/main.js callback.
  46. 46. modular and maintainablebut crappy performance: 7 requests!
  47. 47. make it high-performance
  48. 48. introducing r.jsmodule optimizer.
  49. 49. // build.js({ modules: [ { name: "common" }, { name: "home/main" exclude: "common" } ]})
  50. 50. // run it manually// or as part of automated build processjava -classpath r.js/lib/rhino/js.jar org.mozilla.javascript.tools.shell.Main r.js/dist/r.js -o build.js
  51. 51. // example outputTracing dependencies for: commoncommon.js----------------jquery.jsui/jquery.ui.coreui/jquery.ui.widgetcommon.js
  52. 52. // example outputTracing dependencies for: home/mainhome/main.js----------------ui/jquery.ui.dialoghome/main.js
  53. 53. // example outputUglifying file: common.jsUglifying file: home/main.js
  54. 54. new behind the scenes (phase 1)1. download require.js – 1.2. download home/main.js (includes ui/jquery.ui.dialog) – 2.3. check dependencies.4. download common.js (includes jquery, ui/jquery.ui.core, ui/jquery.ui.widget) – 3.5. check dependencies.
  55. 55. only 3 requests!• only 1 request per page after initial page load (require.js and common.js are cached for all pages).• scripts loads asynchronously (non-blocking) and in parallel.• all assets optimized (supports uglify or closure compiler).
  56. 56. mandatory builds for UI sucks
  57. 57. // build.js({ baseUrl: "js-src/", // input folder dir: "js/", // output folder modules: [ { name: "common" }, { name: "home/main" exclude: "common" } ]})
  58. 58. // index.html<script src="js/require.js" data-main="<?php echo $_GET[dev] ? js-src/home/main : js/home/main ?>"></script>// index.html – production js, 3 requests// index.html?dev - development js, 7 requests
  59. 59. even better performance with has.jsfeature detection library.
  60. 60. define([ has], function($) { // add a test var re = /bdevb/; has.add(dev,re.test(window.location.search)); // use `has` if (has(dev)) { console.log(test); }});
  61. 61. define([ has], function($) { // add a test var re = /bdevb/; has.add(dev,re.test(window.location.search)); // use `has` if (has(dev)) { console.log(test); }});// index.html?dev// "test"
  62. 62. // build.js({ baseUrl: "js-src/", dir: "js/", has: { dev: false }, modules: [ … ]})
  63. 63. // originalif (has(dev)) { console.log(test);}
  64. 64. // originalif (has(dev)) { console.log(test);}// after r.js pre-processingif (false) { console.log(test);}
  65. 65. // originalif (has(dev)) { console.log(test);}// after r.js pre-processingif (false) { console.log(test);}// after uglify post-processing// nothing – uglify strips dead code branches
  66. 66. has.add(ie7-support, true);if (has(ie7-support) { // some godawful hack to fix something in ie7}
  67. 67. make it ultra high-performance
  68. 68. even better performance with almondintended for single page apps or mobile whererequest latency is much worse than desktop.• require.js = 16.5k minified (6k gzipped)• almond.js = 2.3k minified (~1k gzipped)
  69. 69. only 1 request… ever.• shaves 14k of boilerplate.
  70. 70. 1st step to ultra high performanceuse modular programming.• combine with require.js for asynchronous / parallel loading.• automatic concatenation, optimization.• for ultra performance use almond.js.
  71. 71. anyone not use jquery?“Study shows half of all websites use jQuery” – August, 2012
  72. 72. // example of a jquery plugin used with a moduledefine([ jquery, jquery.craftyslide], function($) { $(#slideshow).craftyslide();});
  73. 73. // closer look at `craftyslide.js`$.fn.craftyslide = function (options) { function paginate() { … } function captions() { … } function manual() { … } paginate(); captions(); manual();}
  74. 74. problem with jquery pluginsthey’re a black box.• not easily extendable.• not easily testable.
  75. 75. problem with jquery pluginsthey’re a black box.• not easily extendable.• not easily testable.jquery ui set out to solve this with…
  76. 76. uiwidgets
  77. 77. oh noes! not jquery uibloated piece of crap (210k omg!)• jquery ui is modular – use just the bits you need.• ui core + ui widget + effects core (16k minified or ~6k gzipped).
  78. 78. ui widgetsthe two things plugins suck at, widgets doreally well:• theyre fully extendable.
  79. 79. simple javascript inheritence25 lines of javascript sexiness:• constructors.• object-oriented inheritence.• access to overridden (super) methods.
  80. 80. simple javascript inheritence25 lines of javascript sexiness:• constructors.• object-oriented inheritence.• access to overridden (super) methods.also the foundation of ui widget extensibility.
  81. 81. // example widget$.widget(ui.transformer, { options: { … }, _create: function() { … });
  82. 82. // example widget$.widget(ui.transformer, { options: { … }, _create: function() { … });// extending it$.widget(ui.autobot, $.ui.transformer, { // extend anything or everything});
  83. 83. not-so simple javascript inheritenceeverything from simple javascript inheritence,plus:• namespaces.• public and private methods.• getters/setters.• disable/enable.
  84. 84. ui widgetsthe two things plugins suck at, widgets doreally well:• theyre fully extendable.• theyre tuned for testing.
  85. 85. // if `craftyslide` were a widget$.widget(ui.craftyslide, { _create: function() { … this._paginate(); this._captions(); this._manual(); }, _paginate: function(){ … }, _captions: function(){ … }, _manual: function(){ … });
  86. 86. // adding triggers as hooks for testing$.widget(ui.craftyslide, { … _paginate: function(){ this._trigger(beforePaginate); … this._trigger(afterPaginate); }, …);
  87. 87. // in your unit testfunction beforePaginate() { // test conditions}function afterPaginate() { // test conditions}$(#slideshow).craftyslide({ beforePaginate: beforePaginate, afterPaginate: afterPaginate});
  88. 88. // plugin using `.on()`function manual() { … $pagination.on(click, function (e) { … });}
  89. 89. // plugin using `.on()`function manual() { … $pagination.on(click, function (e) { … });}// widget using `._on()`manual: function() { this._on($pagination, { click: _click }}
  90. 90. // `._on()` remembers all event bindings_on: function( element, handlers ) { … this.bindings = this.bindings.add( element );},
  91. 91. // `._on()` remembers all event bindings_on: function( element, handlers ) { … this.bindings = this.bindings.add( element );},// `.remove()` triggers a `remove` eventthis._on({ remove: "destroy" });
  92. 92. // `._on()` remembers all event bindings_on: function( element, handlers ) { … this.bindings = this.bindings.add( element );},// `.remove()` triggers a `remove` eventthis._on({ remove: "destroy" });// `.destroy()` cleans up all bindings// leaving the DOM pristinedestroy: function() { … this.bindings.unbind( this.eventNamespace );}
  93. 93. // setup widget$(#slideshow).craftyslide();// run tests…// teardown// calls `.destroy()`// which automatically unbinds all bindings$(#slideshow).remove();
  94. 94. high-performance from code re-use
  95. 95. define([ jquery, ui/jquery.ui.core, ui/jquery.ui.widget, ui/jquery.ui.craftyslide], function($) { $.widget(ui.craftyslide, $.ui.craftyslide, { _manual: function() { // extend to do whatever I want } });});
  96. 96. 2nd step to ultra high performanceuse object-oriented widgets as code buildingblocks.• inheritance promotes code re-use, smaller codebase.• built on an architecture that promotes testability.
  97. 97. made possible via• fast file loading.• small file sizes.• avoiding DOM bottlenecks.
  98. 98. avoiding DOM bottlenecks
  99. 99. eventdelegation
  100. 100. <ul id="transformers"> <li><a>Bumblebee</a></li> <li><a>Ratchet</a></li> <li><a>Ironhide</a></li></ul>// typical event binding$(#transformers a).on(click, function() { // do something});
  101. 101. <ul id="transformers"> <li><a>Bumblebee</a></li> <li><a>Ratchet</a></li> <li><a>Ironhide</a></li></ul>// typical event binding$(#transformers a).on(click, function() { // do something});// event bubbling allows us to do this$(#transformers).on(click, function() { // do something});
  102. 102. // event delegation is similar$(#transformers).on(click, a, function() { // do something});
  103. 103. // event delegation is similar$(#transformers).on(click, a, function() { // do something});// but allows us to do this$(document).on(click, #transformers a, function() // do something});
  104. 104. why does that kick ass?• more performant – less memory, faster to bind/unbind.• less maintenance – you can add/remove <ul id="transformers"> at any point in time and dont need to re-attach the event listener.• faster – you can bind the event listener to document as soon as the javascript has loaded, you dont need to wait for domready.
  105. 105. how does this work with widgets?it doesnt – widgets pitfall is they are a DOMbottleneck.
  106. 106. // example `lightbox` widget$(#gallery a).lightbox();// widget depends on `this.element`$.widget(ui.lightbox, { _create: function() { this._on(this.element, { click: show }); }});
  107. 107. two workarounds• one for legacy widgets.• better approach for new widgets.
  108. 108. // legacy widgets$(document).on(click, #gallery a, function() { $(this) .lightbox() .lightbox(show);});
  109. 109. // new widgets$.widget(ui.lightbox, { _create: function() { var sel = this.options.selector; var handler = {}; handler[click + sel] = show’; this._on(handler); }});
  110. 110. // new widgets$.widget(ui.lightbox, { _create: function() { var sel = this.options.selector; var handler = {}; handler[click + sel] = show’; this._on(handler); }});// always instantiate on the document$(document).lightbox({ selector: #gallery a});
  111. 111. 3rd step to ultra high performancedelegate anything and everything you can.• will add interaction to elements that are lazy-loaded, inserted via ajax after page load, etc.• allows for interaction before domready!
  112. 112. delegation isn’t a cure alldelegation works great when the widgetdoesnt need to know about the user up untilthe user interacts with it.but what about widgets that need to affect theDOM on instantiation…
  113. 113. how we’ve done this previously• document.load – the 80s of the internet.• document.DOMContentLoaded – the new load event!
  114. 114. domready considered an anti-pattern“the short story is that we dont want towait for DOMContentReady (or worse theload event) since it leads to bad userexperience. the UI is not responsive untilall the DOM has been loaded from thenetwork. so the preferred way is to useinline scripts as soon as possible” – Google Closure team
  115. 115. <ul id="transformers"> <li><a>Bumblebee</a></li> <li><a>Ratchet</a></li> <li><a>Ironside</a></li></ul><script> $(#transformers).slideshow();</script>
  116. 116. oh no you didn’ta problem with our modular approach:• nothing is exposed to the global scope – you cant use modules from the DOM.
  117. 117. mediator pattern to the rescuea central point of control that modulescommunicate through – instead ofdirectly with each other.
  118. 118. pubsub
  119. 119. central point of control• publish• subscribe• unsubscribe
  120. 120. it’s so easy• publish = $.trigger• subscribe = $.on• unsubscribe = $.off
  121. 121. // in codevar proxy = $({});window.publish = function() { proxy.trigger.apply(proxy, arguments);}window.subscribe = function() { proxy.on.apply(proxy, arguments);}window.unsubcribe = function() { proxy.off.apply(proxy, arguments);}
  122. 122. <ul id="transformers"> <li><a>Bumblebee</a></li> <li><a>Ratchet</a></li> <li><a>Ironside</a></li></ul><script> publish(load.transformers);</script>
  123. 123. define([ main], function() { subscribe(load.transformers, function() { $(#transformers).slideshow(); });});
  124. 124. oh no you didnttwo problems with our modular approach:• nothing is exposed to the global scope – you cant use modules from the DOM.• if the JS is loaded asynchronously you dont know that its available when the browser is parsing the HTML.
  125. 125. <head>// blocking, should be tiny (1k) or inlined!<script src="bootstrap.js"></script>// asynchronous non-blocking<script src="require.js" data-main="home/main"></script>
  126. 126. // bootstrap.js// needs to be some global object// but we can clean it up afterwardsdocument.queue = [];window.publish = function() { document.queue.push(arguments);}
  127. 127. <script> publish(load.transformers);</script>// document.queue = [[load.transformers]]
  128. 128. // main.jsdefine([ jquery], function($) { var proxy = $({}); window.publish = function() { proxy.trigger.apply(proxy, arguments); } window.unsubcribe = function() { proxy.off.apply(proxy, arguments); } …
  129. 129. window.subscribe = function(event) { proxy.on.apply(proxy, arguments);});
  130. 130. window.subscribe = function(event) { proxy.on.apply(proxy, arguments); $(document.queue).each(function(index) { if (this[0] === event) { proxy.trigger.apply(proxy, this); document.queue.splice(index, 1); return false; } });});
  131. 131. ultra high-performance achieved!
  132. 132. ultra high-performance achieved!1. use modular programming.2. use object-oriented widgets as code building blocks.3. delegate anything and everything you can.4. use pubsub for everything else.
  133. 133. onesec
  134. 134. about me
  135. 135. about me• I like Land Cruisers.• lived in Costa Rica for 10 years (there is no excuse for how I speak).• UI dev lead / mobile developer at Backcountry.
  136. 136. questionspreguntas?

×