Embracing YUI 3 & Frontend PerformanceMorgan Cheng, Jan 6th 2011
YUI 3LighterFasterEasier to use
YUI 2 to YUI 3
YUI 2 sample module YAHOO.namespace(‘MyProject’);(function() {var MODULE_NAME=“MyProject”,       Dom = YAHOO.util.Dom;YAHOO.MyProject.MyDialog = function() {      . . .   };}());
Another YUI 2 sample moduleYAHOO.namespace(‘MyProject’);(function() {var MODULE_NAME=“MyProject”,       Dom = YAHOO.util.Dom       Event = YAHOO.util.Event;YAHOO.MyProject.MyPage = {      init: function() {      },      . . .        };Event.onDOMReady(YAHOO.MyProject.MyPage.init, null, YAHOO.MyProject.MyPage);}());
Not Enough for Mash-up Page
LimitationYAHOO property can be overwritten…YAHOO.widget.HelloWorld = something; // by one developer…YAHOO.widget.HelloWorld = another; // by another developer
YUI 3 moduleYUI.add(“myModule”, function(Y) {Y.namespace(‘myproject’).mod = function() {      };   },    “0.0.1”,                            //module version   {requires: [‘node’]}       // dependecies);
Use YUI 3 moduleYUI().use(“myModule”,  function(Y) {varm = new Y.myProject.mod;   . . .});This might be easy to read:varyui = YUI();yui. use(“myModule”, function(Y) {varm = new Y.myProject.mod;   . . .});
SandboxingYUI().use(“myModule”,  function(Y) {varm = new Y.myProject.mod;});YUI().use(“node”,  function(Y) {   //no myProject for this Y});
DOM<div id=“menu”>	  <h1>B1 Canteen Menu</h1>      <ul>             <li>Fish</li>             <li class=“promotion”>Noodle</li>             <li class=“promotion”>Meat</li>             <li>Rice</li>             <li>Tomato</li>      </ul></div>
In YUI 2To get <li> element with “promotion” class under element with id “menu”var Dom = YAHOO.util.Dom;var el = Dom.get(‘menu’);varpromotedItems = Dom.getElementsByClassName(‘promotion’, ‘li’, el);
Selector API in YUI 3One-linerYUI().use(‘node’, function(Y) {varpromtedItems = Y.all(‘#menuli.promotion’);});
Selector Best PracticePrefer “#id”Prefer simple selector over complex selector
YUI 2 Selector Utilityhttp://developer.yahoo.com/yui/selector/But, not chainablereturning raw Dom elements
Node & NodeListWrapper of DOM elementsFrom function-based to object-basedIn YUI 2var el = YAHOO.util.Dom.get(‘id’);YAHOO.util.Dom.addClass(el, ‘className’);In YUI 3Y.one(‘id’).addClass(‘className’);
YUI 3 ChainableYUI().use(‘node’, function(Y) {Y.all(“#menuli”)       .each(function(node, i, ctx) {                if ( i % 2) { node.addClass(‘even’).removeClass(‘disable’);                 }       })       .setStyle(‘color, ‘red’);});
EventEvent FacadeYUI().use(‘event’, ‘node’, function(Y) {Y.one(‘id’).on(‘click’, function(e) {Y.log(e.currentTarget);          e.preventDefault();     });});
   Wait, but how does these stuff get loaded into page?
Seed File<script src="http://yui.yahooapis.com/3.2.0/build/yui/yui-min.js"></script>YUI().use(‘node’, function(Y) {});
Seed File with Loader<script type="text/javascript" src="http://yui.yahooapis.com/combo?3.2.0/build/yui/yui-min.js&3.2.0/build/loader/loader-min.js"></script>YUI().use(‘node’, function(Y) {});
Traditional Way<script type="text/javascript" src="http://yui.yahooapis.com/combo?3.2.0/build/yui/yui-min.js&3.2.0/build/loader/loader-min.js&3.2.0/build/oop/oop-min.js&3.2.0/build/dom/dom-min.js&3.2.0/build/dom/dom-style-ie-min.js&3.2.0/build/event-custom/event-custom-base-min.js&3.2.0/build/event/event-base-min.js&3.2.0/build/pluginhost/pluginhost-min.js&3.2.0/build/node/node-min.js&3.2.0/build/event/event-delegate-min.js"></script>
YUI 3 Dependency MagicRamp-upCombine
JavaScript Blocks
Script Blocks Page Rendering<script type=“text/javascript” src=“long-time.js”></script><imgsrc=“I-am-bocked.png”></img>
Script Blocks Script<script type=“text/javascript” src=“long-time.js”></script><script type=“text/javascript” src=“I-am-bocked.js”></script>    post-IE8 browsers do asynchronous downloading
Non-blocking JavaScriptdefer attributeasync attributeiframedocument.writeXHR injectionCommented JavascriptJavaScript downloaded as Image & Object…
Dynamic Script Tagfunction createScript(js) {var script = document.createElement(‘script’);script.type = ‘text/javascript’;script.async = “true”;var head = document.getElementsByTagname(‘head’)[0];  head.appendChild(script);}
Execution Order MattersExecution order is not guaranteed to be preserved for asynchronous script loadingYUI 3 is born to solve execution order problemeach module payload is not executed until all dependencies are ready
Don’t Get Excited Too Early
Deficiency in YUI 3 LoaderYUI().use blocks each otherYUI().use(‘node’, function() {	. . .});YUI().use(‘event’, function() {    . . .});
Deficiency in YUI 3 LoaderDisabling combo makes each module loaded in sequenceYUI({    combine: false}).use(‘node’, ‘event’, function() {	. . .});
Prefetch YUI Loader hackhttp://bazaar.launchpad.net/~launchpad-pqm/lazr-js/toolchain/annotate/188/src-js/lazrjs/loader/prefetch.js
Flickr Lesson #1Firewall can cut your long URL        http://yui.yahooapis.com/combo?3.2.0/build/yui/yui-min.js&3.2.0/build/oop/oop-min.js&3.2.0/build/event-custom/event-custom-min.js&3.2.0/build/event/event-base-min.js&3.2.0/build/pluginhost/pluginhost-min.js&3.2.0/build/attribute/attribute-min.js&3.2.0/build/dom/dom-min.js&3.2.0/build/dom/dom-style-ie-min.js&3.2.0/build/node/node-min.js&3.2.0/build/event/event-delegate-min.js&3.2.0/build/base/base-min.js&3.2.0/build/anim/anim-min.js&3.2.0/build/node/align-plugin-min.js&3.2.0/build/classnamemanager/classnamemanager-min.js&3.2.0/build/intl/intl-min.js&3.2.0/build/console/lang/console.js&3.2.0/build/event/event-synthetic-min.js&3.2.0/build/event/event-focus-min.js&3.2.0/build/widget/widget-min.js&3.2.0/build/dump/dump-min.js&3.2.0/build/substitute/substitute-min.js&3.2.0/build/console/console-min.js&3.2.0/build/plugin/plugin-min.js&3.2.0/build/console/console-filters-min.js&3.2.0/build/cookie/cookie-min.js&3.2.0/build/dom/dom-style-ie-min.js&3.2.0/build/event/event-resize-min.js&3.2.0/build/event/event-mouseenter-min.js&3.2.0/build/dd/dd-min.js&3.2.0/build/dd/dd-gestures-min.js&3.2.0/build/dd/dd-drop-plugin-min.js&3.2.0/build/dd/dd-plugin-min.js&3.2.0/build/event/event-touch-min.js&3.2.0/build/event-gestures/event-move-min.js&3.2.0/build/dd/dd-gestures-min.js&3.2.0/build/dataschema/dataschema-base-min.js&3.2.0/build/dataschema/dataschema-array-min.js&3.2.0/build/cache/cache-base-min.js&3.2.0/build/querystring/querystring-stringify-simple-min.js&3.2.0/build/io/io-base-min.js&3.2.0/build/json/json-min.js&3.2.0/build/dataschema/dataschema-json-min.js&3.2.0/build/dataschema/dataschema-text-min.js&3.2.0/build/dataschema/dataschema-xml-min.js&3.2.0/build/datasource/datasource-min.js
Flickr Lesson #2Sexy URL is not welcomehttp://… &xxw.js&xxx.js&…
Multiple PagesPage Amodule A + B + C + D + E + XPage Bmodule A + B + C + D + E + YPage Cmodule A + B + C + D + Z
YUI 3 in Action
Widget Class Diagram
Code ReuseY.extendY.augmentY.clone      	//deep copy by valueY.merge		//mix-in multiple objectsY.aggregate
Powerful Y.mixY.mix(r, s, ov, wl, mode, merge)mode:0. object to object1. prototype to prototype2. prototype to prototype and object props3. prototype to object 4. object to prototype
EventTargetpublishfireonbeforeafter
Attributesetget
BaseInitializerdestrutorstatic property NAME as event prefix
WidgetrenderrenderUIbindUIsyncUI
PluginY.namespace('QPlus').DigPlugin = Y.Base.create('Dig', Y.Plugin.Base, [], { 	//prototypal properties},{    // static properties});
Plugin Prototypal PropertiesInitializerdestuctor
Plugin Static PropertiesNSMandatoryUsed as host property name to reference plugin
Use PluginplugTrigger initializerunplugTrigger destructor
AOP in YUI3doBefore/onHostEvent/doAfterY.SamplePlugin = Y.Base.create(‘sample’, Y.Plugin.Base, [],{initializer: function() {this.doBefore(‘someMethod’, function() { … } );    }},{    NS: ‘sample’});
Custom EventApplication LevelDe-coupling Identified by string
YUI 2 Custom Eventthis.myEvent = new YAHOO.util.CustomEvent(‘myEvent’);this.myEvent.fire();instance.myEvent.subscribe(doSomething);
YUI 3 Custom EventY.fire(‘chengguan-coming’, 1, 2, 3);Y.on(‘chengguan-coming’, function(arg1, arg2, arg3) {    . . .});
Advanced Custom Eventthis.publish(‘myEvent’, {     broadcast: 2,  //broadcast to all YUI instancesdefaultFn: handler // default event handler} );this.fire(‘myEvent’);instance.on(‘myEvent’, doSomething);
Consistent Event APIinstance.on(‘click’, onClick);instance.on(‘myEvent’, doSomething);
Synthetic EventExtend DOM eventY.Event.define(‘flick’, {     on: function(node, subscription, notifier) {     },     detach: function(node, subscription notifier) {      }});
Frontend Performance Improvments
No iframeiframe is slowiframerequires one more HTTP roundtripiframe makes your application complicated
Flush Early Flush early and user view result earlyFlush early and external resources starts downloading early
MVC might hurt your performanceModelViewController
How ySymfony MVC works<head></head><body>  <div id=“hd”></div>  <div id=“bd”>    <?php echo $sf_content?>  </div>  <div id=“ft”></div></body>LibActionTemplate
Enable Early Flush in ySymfonyDon’t use layoutsetLayout(false)Fetch partial and flush it in ActiongetPartialflushCustomize web response class to avoid head() after flush()
Don’t<html>    . . .	<body>		<div>              <div id=‘hd’></div>              <?php flush(); ?>		      <? // some browser would not render till getting closing tag ?>              <div id=‘bd’></div>              <?php flush(); ?>              <div id=‘ft’></div>		</div>	</body></html>
Do<html>    . . .	<body>              <div id=‘hd’></div>              <?php flush(); ?>              <div id=‘bd’></div>              <?php flush(); ?>              <div id=‘ft’></div>	</body></html>
JavaScript Loading Strategy
JavaScript Loading StrategyDynamic Script TagConcatenate JavaScript into single file with FUSE
Users are Not PatientUser might start interact with the page before its JavaScript module is loadedIt is not good to not respond to user interaction
Put User Actions into Queue
Event-Binder: A Tale of Two JavaScriptsInline ScriptPrevent default behaviorShow progressive UIPut event into queueExternal ScriptFlush events from queueHide progressive UIRemove former event handler
Just at Scratch Line
YUI 3 Resourceshttp://developer.yahoo.com/yui/3/http://yuiblog.com/http://twitter.com/miraglia/yui/membersyui-users@yahoo-inc.com
Web Performance Resourceshttp://stevesouders.com/http://www.perfplanet.com/Velocity
Thanks

Embracing YUI3 and Frontend Perf

  • 1.
    Embracing YUI 3& Frontend PerformanceMorgan Cheng, Jan 6th 2011
  • 2.
  • 3.
  • 4.
    YUI 2 samplemodule YAHOO.namespace(‘MyProject’);(function() {var MODULE_NAME=“MyProject”, Dom = YAHOO.util.Dom;YAHOO.MyProject.MyDialog = function() { . . . };}());
  • 5.
    Another YUI 2sample moduleYAHOO.namespace(‘MyProject’);(function() {var MODULE_NAME=“MyProject”, Dom = YAHOO.util.Dom Event = YAHOO.util.Event;YAHOO.MyProject.MyPage = { init: function() { }, . . . };Event.onDOMReady(YAHOO.MyProject.MyPage.init, null, YAHOO.MyProject.MyPage);}());
  • 6.
    Not Enough forMash-up Page
  • 7.
    LimitationYAHOO property canbe overwritten…YAHOO.widget.HelloWorld = something; // by one developer…YAHOO.widget.HelloWorld = another; // by another developer
  • 8.
    YUI 3 moduleYUI.add(“myModule”,function(Y) {Y.namespace(‘myproject’).mod = function() { }; }, “0.0.1”, //module version {requires: [‘node’]} // dependecies);
  • 9.
    Use YUI 3moduleYUI().use(“myModule”, function(Y) {varm = new Y.myProject.mod; . . .});This might be easy to read:varyui = YUI();yui. use(“myModule”, function(Y) {varm = new Y.myProject.mod; . . .});
  • 10.
    SandboxingYUI().use(“myModule”, function(Y){varm = new Y.myProject.mod;});YUI().use(“node”, function(Y) { //no myProject for this Y});
  • 11.
    DOM<div id=“menu”> <h1>B1 Canteen Menu</h1> <ul> <li>Fish</li> <li class=“promotion”>Noodle</li> <li class=“promotion”>Meat</li> <li>Rice</li> <li>Tomato</li> </ul></div>
  • 12.
    In YUI 2Toget <li> element with “promotion” class under element with id “menu”var Dom = YAHOO.util.Dom;var el = Dom.get(‘menu’);varpromotedItems = Dom.getElementsByClassName(‘promotion’, ‘li’, el);
  • 13.
    Selector API inYUI 3One-linerYUI().use(‘node’, function(Y) {varpromtedItems = Y.all(‘#menuli.promotion’);});
  • 14.
    Selector Best PracticePrefer“#id”Prefer simple selector over complex selector
  • 15.
    YUI 2 SelectorUtilityhttp://developer.yahoo.com/yui/selector/But, not chainablereturning raw Dom elements
  • 16.
    Node & NodeListWrapperof DOM elementsFrom function-based to object-basedIn YUI 2var el = YAHOO.util.Dom.get(‘id’);YAHOO.util.Dom.addClass(el, ‘className’);In YUI 3Y.one(‘id’).addClass(‘className’);
  • 17.
    YUI 3 ChainableYUI().use(‘node’,function(Y) {Y.all(“#menuli”) .each(function(node, i, ctx) { if ( i % 2) { node.addClass(‘even’).removeClass(‘disable’); } }) .setStyle(‘color, ‘red’);});
  • 18.
    EventEvent FacadeYUI().use(‘event’, ‘node’,function(Y) {Y.one(‘id’).on(‘click’, function(e) {Y.log(e.currentTarget); e.preventDefault(); });});
  • 19.
    Wait, but how does these stuff get loaded into page?
  • 20.
  • 21.
    Seed File withLoader<script type="text/javascript" src="http://yui.yahooapis.com/combo?3.2.0/build/yui/yui-min.js&3.2.0/build/loader/loader-min.js"></script>YUI().use(‘node’, function(Y) {});
  • 22.
    Traditional Way<script type="text/javascript"src="http://yui.yahooapis.com/combo?3.2.0/build/yui/yui-min.js&3.2.0/build/loader/loader-min.js&3.2.0/build/oop/oop-min.js&3.2.0/build/dom/dom-min.js&3.2.0/build/dom/dom-style-ie-min.js&3.2.0/build/event-custom/event-custom-base-min.js&3.2.0/build/event/event-base-min.js&3.2.0/build/pluginhost/pluginhost-min.js&3.2.0/build/node/node-min.js&3.2.0/build/event/event-delegate-min.js"></script>
  • 23.
    YUI 3 DependencyMagicRamp-upCombine
  • 24.
  • 25.
    Script Blocks PageRendering<script type=“text/javascript” src=“long-time.js”></script><imgsrc=“I-am-bocked.png”></img>
  • 26.
    Script Blocks Script<scripttype=“text/javascript” src=“long-time.js”></script><script type=“text/javascript” src=“I-am-bocked.js”></script> post-IE8 browsers do asynchronous downloading
  • 27.
    Non-blocking JavaScriptdefer attributeasyncattributeiframedocument.writeXHR injectionCommented JavascriptJavaScript downloaded as Image & Object…
  • 28.
    Dynamic Script TagfunctioncreateScript(js) {var script = document.createElement(‘script’);script.type = ‘text/javascript’;script.async = “true”;var head = document.getElementsByTagname(‘head’)[0]; head.appendChild(script);}
  • 29.
    Execution Order MattersExecutionorder is not guaranteed to be preserved for asynchronous script loadingYUI 3 is born to solve execution order problemeach module payload is not executed until all dependencies are ready
  • 30.
  • 31.
    Deficiency in YUI3 LoaderYUI().use blocks each otherYUI().use(‘node’, function() { . . .});YUI().use(‘event’, function() { . . .});
  • 32.
    Deficiency in YUI3 LoaderDisabling combo makes each module loaded in sequenceYUI({ combine: false}).use(‘node’, ‘event’, function() { . . .});
  • 33.
    Prefetch YUI Loaderhackhttp://bazaar.launchpad.net/~launchpad-pqm/lazr-js/toolchain/annotate/188/src-js/lazrjs/loader/prefetch.js
  • 34.
    Flickr Lesson #1Firewallcan cut your long URL http://yui.yahooapis.com/combo?3.2.0/build/yui/yui-min.js&3.2.0/build/oop/oop-min.js&3.2.0/build/event-custom/event-custom-min.js&3.2.0/build/event/event-base-min.js&3.2.0/build/pluginhost/pluginhost-min.js&3.2.0/build/attribute/attribute-min.js&3.2.0/build/dom/dom-min.js&3.2.0/build/dom/dom-style-ie-min.js&3.2.0/build/node/node-min.js&3.2.0/build/event/event-delegate-min.js&3.2.0/build/base/base-min.js&3.2.0/build/anim/anim-min.js&3.2.0/build/node/align-plugin-min.js&3.2.0/build/classnamemanager/classnamemanager-min.js&3.2.0/build/intl/intl-min.js&3.2.0/build/console/lang/console.js&3.2.0/build/event/event-synthetic-min.js&3.2.0/build/event/event-focus-min.js&3.2.0/build/widget/widget-min.js&3.2.0/build/dump/dump-min.js&3.2.0/build/substitute/substitute-min.js&3.2.0/build/console/console-min.js&3.2.0/build/plugin/plugin-min.js&3.2.0/build/console/console-filters-min.js&3.2.0/build/cookie/cookie-min.js&3.2.0/build/dom/dom-style-ie-min.js&3.2.0/build/event/event-resize-min.js&3.2.0/build/event/event-mouseenter-min.js&3.2.0/build/dd/dd-min.js&3.2.0/build/dd/dd-gestures-min.js&3.2.0/build/dd/dd-drop-plugin-min.js&3.2.0/build/dd/dd-plugin-min.js&3.2.0/build/event/event-touch-min.js&3.2.0/build/event-gestures/event-move-min.js&3.2.0/build/dd/dd-gestures-min.js&3.2.0/build/dataschema/dataschema-base-min.js&3.2.0/build/dataschema/dataschema-array-min.js&3.2.0/build/cache/cache-base-min.js&3.2.0/build/querystring/querystring-stringify-simple-min.js&3.2.0/build/io/io-base-min.js&3.2.0/build/json/json-min.js&3.2.0/build/dataschema/dataschema-json-min.js&3.2.0/build/dataschema/dataschema-text-min.js&3.2.0/build/dataschema/dataschema-xml-min.js&3.2.0/build/datasource/datasource-min.js
  • 35.
    Flickr Lesson #2SexyURL is not welcomehttp://… &xxw.js&xxx.js&…
  • 36.
    Multiple PagesPage AmoduleA + B + C + D + E + XPage Bmodule A + B + C + D + E + YPage Cmodule A + B + C + D + Z
  • 37.
    YUI 3 inAction
  • 38.
  • 39.
    Code ReuseY.extendY.augmentY.clone //deep copy by valueY.merge //mix-in multiple objectsY.aggregate
  • 40.
    Powerful Y.mixY.mix(r, s,ov, wl, mode, merge)mode:0. object to object1. prototype to prototype2. prototype to prototype and object props3. prototype to object 4. object to prototype
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
    PluginY.namespace('QPlus').DigPlugin = Y.Base.create('Dig',Y.Plugin.Base, [], { //prototypal properties},{ // static properties});
  • 46.
  • 47.
    Plugin Static PropertiesNSMandatoryUsedas host property name to reference plugin
  • 48.
  • 49.
    AOP in YUI3doBefore/onHostEvent/doAfterY.SamplePlugin= Y.Base.create(‘sample’, Y.Plugin.Base, [],{initializer: function() {this.doBefore(‘someMethod’, function() { … } ); }},{ NS: ‘sample’});
  • 50.
  • 51.
    YUI 2 CustomEventthis.myEvent = new YAHOO.util.CustomEvent(‘myEvent’);this.myEvent.fire();instance.myEvent.subscribe(doSomething);
  • 52.
    YUI 3 CustomEventY.fire(‘chengguan-coming’, 1, 2, 3);Y.on(‘chengguan-coming’, function(arg1, arg2, arg3) { . . .});
  • 53.
    Advanced Custom Eventthis.publish(‘myEvent’,{ broadcast: 2, //broadcast to all YUI instancesdefaultFn: handler // default event handler} );this.fire(‘myEvent’);instance.on(‘myEvent’, doSomething);
  • 54.
    Consistent Event APIinstance.on(‘click’,onClick);instance.on(‘myEvent’, doSomething);
  • 55.
    Synthetic EventExtend DOMeventY.Event.define(‘flick’, { on: function(node, subscription, notifier) { }, detach: function(node, subscription notifier) { }});
  • 56.
  • 57.
    No iframeiframe isslowiframerequires one more HTTP roundtripiframe makes your application complicated
  • 58.
    Flush Early Flushearly and user view result earlyFlush early and external resources starts downloading early
  • 59.
    MVC might hurtyour performanceModelViewController
  • 60.
    How ySymfony MVCworks<head></head><body> <div id=“hd”></div> <div id=“bd”> <?php echo $sf_content?> </div> <div id=“ft”></div></body>LibActionTemplate
  • 61.
    Enable Early Flushin ySymfonyDon’t use layoutsetLayout(false)Fetch partial and flush it in ActiongetPartialflushCustomize web response class to avoid head() after flush()
  • 62.
    Don’t<html> . . . <body> <div> <div id=‘hd’></div> <?php flush(); ?> <? // some browser would not render till getting closing tag ?> <div id=‘bd’></div> <?php flush(); ?> <div id=‘ft’></div> </div> </body></html>
  • 63.
    Do<html> . . . <body> <div id=‘hd’></div> <?php flush(); ?> <div id=‘bd’></div> <?php flush(); ?> <div id=‘ft’></div> </body></html>
  • 64.
  • 65.
    JavaScript Loading StrategyDynamicScript TagConcatenate JavaScript into single file with FUSE
  • 66.
    Users are NotPatientUser might start interact with the page before its JavaScript module is loadedIt is not good to not respond to user interaction
  • 67.
  • 68.
    Event-Binder: A Taleof Two JavaScriptsInline ScriptPrevent default behaviorShow progressive UIPut event into queueExternal ScriptFlush events from queueHide progressive UIRemove former event handler
  • 69.
  • 70.
  • 71.
  • 72.