LAB.js Loading And Blocking JavaScript

百度随身听用到的一个js工具,附官网使用说明。

http://labjs.com/documentation.php

Test Suite Passes

Browsers tested and passing the Test Suite (please help to fill in gaps):

Windows: IE 6+, FF 1.5+, Chrome (latest), Opera 9.6+, Safari 4+, Netscape 7.2+, Mozilla 1.7

Mac (OSX): Safari 3.2+, Netscape 9, FF 1.5+, Opera 9.6+, Mozilla 1.7, Camino 2

Linux: FF 1.5+, Opera 9.5+, Netscape 9

Known unsupported

Safari 2, Opera 8, Konquerer (3.4, 4.1), FF 4b7 (only this exact release. <= b6 and b8+ are ok), Blackberry 4

Builds

It is recommended that you simply use the distribution-included minified files LAB.min.js and LAB-debug.min.js in production and development, respectively. However, some developers may want to use their own minification tools to "build" LAB.min.js themselves, from the source file (LAB.src.js). This is fine, but make sure you don't use too aggressive of a minifier/compiler, as it's possible you could alter something about LABjs in an unexpected and breaking way. Typically, compressors like YUIC and Packer (without base62) are quite safe and should be fine.

As of v2.0, LABjs source code (LAB.src.js) has DEBUG-mode code in it, delimited by /!*START_DEBUG*/ and /*!END_DEBUG*/ comments. This debug code is intended to be removed if minifying and building LAB.min.js from the original source code (but left in if building LAB-debug.min.js). This removal is possible with a simple preprocessing regex replace to remove the DEBUG-mode code from the source before minification. For instance, the following regular expression should work for that purpose:

//*!START_DEBUG(?:.|n)+END_DEBUG*//

The LAB-debug.min.js file skipped this step before minification, thus it includes the debug code. The LAB.min.js file however has the DEBUG-mode code stripped from it.

NOTE: It should also be safe to simply pass LAB.min.js or LAB-debug.min.js to your own minifier (even though they are already minified), which will simply do a harmless re-minification of the file. The best option, however, would be to instruct your build process to ignore minification on these files.

Documentation

Old and busted:

<script src="framework.js"></script>

<script src="plugin.framework.js"></script>

<script src="myplugin.framework.js"></script>

<script src="init.js"></script>

New hotness:

<script>

  $LAB

  .script("framework.js").wait()

  .script("plugin.framework.js")

  .script("myplugin.framework.js").wait()

  .script("init.js").wait();

</script>

In the above example, all scripts load in parallel (by default). "framework.js" needs to execute first, before the others. But "plugin.framework.js" and "myplugin.framework.js" have no dependencies between them, so their execution order is not important (first-come, first-served). "init.js" needs to wait for all 3 scripts to execute before it runs.

There are a few other variations on the .script(…) signature. For instance, you don't have to do a single script() call for each file (though I think it makes thing more readable). You can pass as many scripts singularly as parameters to one script() call. You can also pass an array of scripts, and it will loop through them and load them in the same way. Lastly, you can pass in an object instead of string, and the object literal can contain "src", "type", and "charset" specifications, if you need to override the defaults (for instance, load a "text/vbscript" script).

How to deal with inline scripts

So, there's some special gotchas you need to look out for when using LABjs not only to load scripts but also to "couple" inline scripts to execute in the proper order with the loaded scripts. Take this expanded example from above:

<script src="framework.js"></script>

<script src="plugin.framework.js"></script>

<script src="myplugin.framework.js"></script>

<script>

  myplugin.init();

</script>

<script>

  framework.init();

  framework.doSomething();

</script>

In this example, the browser would normally make sure that the two inline script blocks at the end will not even be parsed/executed until after the 4 scripts have been loaded. So, the references to "myplugin.init", "framework.init", and "framework.doSomething" will already be guaranteed to be defined (in the scripts that are loaded before the code is parsed).

But, because LABjs is an asynchronous loader, this "guarantee" will not be true. So, this code will fail:

<script>

  $LAB

  .script("framework.js").wait()

  .script("plugin.framework.js")

  .script("myplugin.framework.js")

</script>

<script>

  myplugin.init();

</script>

<script>

  framework.init();

  framework.doSomething();

</script>

The reason is because "myplugin.init", "framework.init", and "framework.doSomething" will be undefined symbols the instant after the $LAB chain first executes and starts loading the scripts. To make this code asynchronous safe, we do this instead:

<script>

  $LAB

  .script("framework.js").wait()

  .script("plugin.framework.js")

  .script("myplugin.framework.js")

  .wait(function(){

     myplugin.init();

     framework.init();

     framework.doSomething();

  });

</script>

We wrap the inline script code in a ".wait(function(){ … })" wrapper, which will defer its execution until the proper time in the loading order of the chain.

A simple pattern for converting <script src="…"></script> and <script>… /* inline code */ …</script> tags into $LAB API calls, use these two rules:

For every <script src="…"></script> tag you are replacing, you should have a ".script(…)" call

For every <script>… /*inline code*/ …</script> inline script block with code in it, we need a ".wait(function(){ … })" call to wrap around the code

If this still is a little confusing, it's ok! Read more about converting inline synchronous code to LABjs suitable asynchronous code over on getiblog.

Special "Feature" Note(s)

LABjs is able to handle non-feature-testable behavior in older legacy browsers through the use of two small but fairly solid browser inference sniffs. This is different from feature-detection (which is always preferable when possible), but with script-loading quirk behavior, there's no way to feature-detect (in those older legacy browsers), so we simply have to fork behavior based on browser family. So, the following two browser inference sniffs are used:

opera_or_gecko = (global.opera && Object.prototype.toString.call(global.opera) == "[object Opera]") ||

("MozAppearance" in document.documentElement.style),

The Opera inference relies on the fact that Opera declares an "opera" property on the window object, and that it's a special native, whose toString() return value cannot be duplicated by someone just declaring a window.opera property themselves. This gives a reasonably strong inference that we are in fact in the Opera browser.

The Mozilla inference relies on "MozAppearance" only appearing in Gecko browsers in the documentElement's style object. This isn't as solid as the Opera inference, but Gecko/FF has had this property for a really long time and there's a good chance they won't remove it, nor would any other browser have any reason to add a property of that name.

LABjs feature-tests for `async=true` (aka "ordered async") on script-inserted script elements. Read more about this feature on the Dynamic Script Execution Order WHATWG Wiki Page and Henri Sivonen's HTML5 Script Execution Changes.

The HTML Spec has officially been updated to include the "async=false" behavior, that LABjs employs a feature-detect for. FF4+, Webkit (March 2011+), IE10p2+, and Chrome 12+ now have this implemented (and Opera is coming soon).

LABjs also feature-tests for "real preloading". This type of preloading doesn't rely on the "cache preloading" hacks, but instead directly preloads the script (once) and then allows the execution of that code on-demand without a second (cached) request. This functionality has been present in IE since version 4, is suggested in the HTML spec (not required), and has been proposed to be officially declared a requirement. Even if it's never actually standardized any further, it's the best method available for IE, so it's worth it. Read more about the proposal to make this a requirement: Script Execution Control.

LABjs API

Methods

$LAB.setGlobalDefaults()

$LAB.setOptions()

$LAB.script()

$LAB.wait()

$LAB.queueScript()

$LAB.queueWait()

$LAB.runQueue()

$LAB.noConflict()

$LAB.sandbox()

void $LAB.setGlobalDefaults(object optionsObject)

Sets one or more options as global defaults to be used by all $LAB chains on the page. This method is not chainable, and thus must only be called stand-alone like this: $LAB.setGlobalDefaults({…});

Parameters

optionsObject : an object which contains name/value pairs for the options to set:

AlwaysPreserveOrder: a boolean (defaults to false) which controls whether an implicit empty wait() call is assumed after every script loading, which essentially forces all scripts in the chain to execute serially in order (loading in parallel, by default, is not affected by this setting).

UseLocalXHR: a boolean (defaults to true) which controls whether LABjs will use an XHR Ajax call to "preload" scripts which are "local" to the current page's domain (note: does *not* take advantage of any cross-domain Ajax techniques). This "preload" is effective and quite performant for local scripts.

Note: This technique is only attempted to be used if it's set to true and in an older Webkit browser, where neither ordered-async or real-preloading were present, and then only on a local same-domain script. Otherwise, this setting is ignored.

AllowDuplicates: a boolean (defaults to false) which controls whether or not LABjs will inspect its internal script URL cache to prevent a script URI from being (accidentally, most likely) loaded a second time. By default, LABjs will not make any duplicate requests for the same script URL. Duplicates within the same chain will simply be ignored, and duplicates across other chains will cause those duplicate calls in the other chains to patiently wait until the single resource loading finishes. If you turn "AllowDuplicates" on, then no such de-duplication will be done, either within the chain or between multiple chains.

BasePath: a string (defaults to empty "") which specifies a path value to prepend to every script URL. All relative script URL's are already automatically fully qualified to the page's location, so this BasePath should really be used to set some other absolute path to override this qualification for otherwise relative "looking" script URL's.

CacheBust: a boolean (defaults to false) which adds a random number parameter to every script URL to prevent the URL you requested from being cached. In other words, your requested script will be loaded new each time, since it will have a new random number on the URL each time.

Debug: a boolean (defaults to false) which controls if the web console will be used to log the various steps of loading/processing logic for $LAB chains. This option is only effective if using the source file for LABjs, or the LAB-debug.min.js file. Otherwise, this setting will be ignored. As such, it's safe to have this option turned on in both development and production, but not serve the debug build of the file to production unless you need to troubleshoot an issue in production.

Returns

none

[$LAB] $LAB.setOptions(object optionsObject)

Sets one or more options only to be in effect for the current $LAB chain being executed. This method is chainable (in fact, for it to have any effect, it must be part of a chain!), but it must be the first call in the chain (as changing many of the options mid-chain would create race conditions). So, it must be called like this: $LAB.setOptions({…}).script(…)…;

Parameters

optionsObject : an object which contains name/value pairs for the options to set

Returns

$LAB : the chained object reference so subsequent calls to script() and wait() can be made.

[$LAB] $LAB.script(varies,…)

This method accepts one or more parameters of varying types. Each parameter value is intended to specify a script resource URI to load.

Parameters

varies (can be any number/combination of the following):

string : a relative or absolute path URI to a local or remote script to load

object : an object containing one or more of these properties:

string src : (required) a relative or absolute path URI to a local or remote script to load

string type : (optional) the MIME type attribute (ie, "text/javascript", "text/vbscript", etc)

string charset : (optional) the charset attribute (ie, "utf-8", etc)

bool allowDup : (optional) whether to suppress a duplicate script URI check for this script. (defaults to the value of the AllowDuplicates global or chain option.

array : an array of parameters of any of the allowed types (including array)

function : if a function is found as one of the parameters, that function will be be executed immediately, which must return a value directly. The return value must be one of the other allowable types (string, object, or array). If the function call results in no return value ("undefined") or the value is "falsy" (false, null, etc), it will be interpreted as no script to load.

This feature allows the construction of "conditional chains", where run-time logic is used to decide ultimately what scripts will be loaded by the $LAB chain.

See Example 8 below for usage demonstration.

Returns

$LAB : the chained object reference so subsequent calls to script() and wait() can be made.

[$LAB] $LAB.wait(function inlineScript[=null])

This function serves two purposes. Firstly, when inserted into a chain, it tells the chain to ensure that all scripts previously listed in the chain should finish executing before allowing the internal 'execution cursor' to proceed to the remaining part of the chain. Essentially, you can specify "blocking" behavior in terms of execution (not loading!) to ensure dependencies execute in the proper order (regardless of how they were loaded).

Secondly, you can specify a function reference (usually an inline anonymous function) that will be executed in order, immediately following the chain's previous scripts executing, but before subsequent scripts in the chain are executed. This is synonymous with inline <script> tags before or after <script src="…"> type tags.

For instance, it can be used to defer the initialization execution of a script you are downloading, like this: $LAB.script("script.js").wait(function(){initScript();}); where initScript() is a function that is defined in "script.js".

Parameters

inlineScript : (optional, defaults to null)

Returns

$LAB : the chained object reference so subsequent calls to script() and wait() can be made.

[$LAB instance] $LAB.queueScript(varies,…)

This function queues up a call just like it was made against script(), except that the queueing prevents it from being processed right away. The parameters you pass to this function are passed directly to script() once the queue is processed.

Parameters

varies : the same parameters for script()

Returns

$LAB instance : The initial $LAB instance, so subsequent calls to queueScript() and queueWait() can be made.

[$LAB instance] $LAB.queueWait(function inlineScript[=null])

This function queues up a call just like it was made against wait(), except that the queueing prevents it from being processed right away. The parameters you pass to this function are passed directly to wait() once the queue is processed.

Parameters

inlineScript : the same parameters for wait()

Returns

$LAB instance : The initial $LAB instance, so subsequent calls to queueScript() and queueWait() can be made.

[$LAB] $LAB.runQueue(none)

Calls to queueScript() and queueWait() queue up the calls just like you are creating a queued $LAB chain. Calling runQueue() will then process the chain as if it had been directly defined. The return value is the end of the $LAB chain, so it can be further chained as you see fit.

Parameters

none

Returns

$LAB : the chained object reference from the queued chain, so subsequent calls to script() and wait() can be made.

[$LAB instance] $LAB.noConflict(none)

noConflict() rolls back the page to the previous version of LABjs (if any) before this copy of LABjs was loaded, and returns the current instance of $LAB.

Parameters

none

Returns

$LAB instance : the current $LAB instance (from before the rollback)

[$LAB instance] $LAB.sandbox(none)

sandbox() creates a new clean instance of $LAB. This allows you to get new instances of LAB, if for instance you need a new queue for use with queueScript() or queueWait().

Parameters

none

Returns

$LAB instance : a new clean instance of $LAB

Examples

Example 1:

$LAB

.script("script1.js")

.script("script2.js")

.script("script3.js")

.wait(function(){ // wait for all scripts to execute first

script1Func();

script2Func();

script3Func();

});

Example 2:

$LAB

.script({ src: "script1.js", type: "text/javascript" })

.script("script2.js")

.script("script3.js")

.wait(function(){ // wait for all scripts to execute first

script1Func();

script2Func();

script3Func();

});

Example 3:

$LAB

.script("script1.js", "script2.js", "script3.js")

.wait(function(){ // wait for all scripts to execute first

script1Func();

script2Func();

script3Func();

});

Example 4:

$LAB

.script( [ "script1.js", "script2.js" ], "script3.js")

.wait(function(){ // wait for all scripts to execute first

script1Func();

script2Func();

script3Func();

});

Example 5:

$LAB

.script("script1.js").wait() // empty wait() simply ensures execution order be preserved for this script

.script("script2.js") // both script2 and script3 depend on script1 being executed before

.script("script3.js").wait() // but are not dependent on each other and can execute in any order

.script("script4.js") // depends on script1, script2 and script3 being loaded before

.wait(function(){script4Func();});

Example 6:

$LAB

.script("script1.js") // script1, script2, and script3 do not depend on each other,

.script("script2.js") // so execute in any order

.script("script3.js")

.wait(function(){  // can still have executable wait(…) functions if you need to

alert("Scripts 1-3 are loaded!");

})

.script("script4.js") // depends on script1, script2 and script3 being executed before

.wait(function(){script4Func();});

Example 7:

$LAB

.setOptions({AlwaysPreserveOrder:true}) // tells this chain to implicitly "wait" on

                                       // execution (not loading) between each script

.script("script1.js") // script1, script2, script3, and script4 *DO* depend on each

.script("script2.js") // other, and will execute serially in order after all 4 have have

.script("script3.js") // loaded in parallel

.script("script4.js")

.wait(function(){script4Func();});

Example 8:

$LAB

.script(function(){

// assuming `_is_IE` defined by host page as true in IE and false in other browsers

if (_is_IE) {

return "ie.js"; // only if in IE, this script will be loaded

}

else {

return null; // if not in IE, this script call will effectively be ignored

}

})

.script("script1.js")

.wait();

Example 9:

$LAB

.queueScript("script1.js") // script1, script2, and script3 do not depend on each other,

.queueScript("script2.js") // so execute in any order

.queueScript("script3.js")

.queueWait(function(){  // can still have executable wait(…) functions if you need to

alert("Scripts 1-3 are loaded!");

})

.queueScript("script4.js") // depends on script1, script2 and script3 being executed before

.queueWait(function(){script4Func();});

// …

$LAB

.runQueue() // execute the queue as a $LAB chain

.script("script5.js")

.wait(function(){

alert("Script 5 loaded and executed off the end of a previously queued chain.");

});

Release Notes

2.0.3

Fixed Bug #43 regression bug in IE<=8.

2.0.2

Fixed Bug #36 about race conditions with `.wait(…)` callbacks.

Fixed Bug #38 about incorrect handling of array parameters to `.script(…)` calls.

2.0.1

Fixed Bug #35 affecting IE6 and IE7 regarding path handling.

2.0

This is a complete ground-up rewrite of LABjs. The API is backwards-compatible (except for a few configuration options being removed). Many bugs were fixed and several new features were added.

`AllowDuplicates` now actually works across chains. This is very important for those who want to use multiple/nested $LAB chains as part of a shared-dependency loading mechanism.

Chains are now fully resumable, meaning you can save the return value from the last call to a chained function, and then use that saved value later as the starting point to resume the chain from where it left off.

Queueing is now built-in, with `queueScript`, `queueWait` and `runQueue` — this important for those who want to build up the chain across multiple files or inline <script> elements (like in the CMS case), but want to defer starting the loading of the code starting until later (usually at the bottom of the page).

LABjs now supports `noConflict` (for rolling back to a previous version/copy of $LAB on the page) and `sandbox` (for creating a new pristine sandboxed copy of the current $LAB)

LABjs now relies on feature-testing for `async=false` and implicit/explicit "true preloading" (currently only IE, but in the spec process). Ugly/hacky "cache preloading" is now only used for "older webkit" (before March 2011 nightlies, etc), and even then, only for remote files.

For XHR preloading (only used in "older webkit" for local files, by default), to support better debugability, "// @sourceURL=…" is appended to the end of the code, to map the XHR/injected code to a real file name. Currently, browsers only support this for eval() (not script injection, like LABjs uses). It is hoped that browsers will soon support this annotation for their developer-tools.

Speaking of debugging, LABjs now supports a DEBUG mode (only if you use the source file, or if you use the LABjs-debug.min.js production file) *and* enable the "Debug" config option, which captures all the inner workings (and any errors in .wait() calls) to the browser's console.log, if present.

LABjs now supports a "CacheBust" config option, which will attempt to make sure all loaded scripts are forcibly loaded new on each page refresh, by auto-appending a random number parameter to each URL. This is really only practical/advised for DEV environments, where you want to ensure that the code reloads every time. Doing so in production would be really bad for user performance.

As part of LABjs' rewrite, the code style is now significantly improved in readability (most "minification" hacks have been removed), and it's also using more memory-savvy code, such as far fewer closures. As a result, LABjs should run leaner and faster, if only by a little bit. The goal is to get LABjs out of the way so your scripts load and run as fast as possible.

"AppendTo", "UsePreloading", and "UseCachePreloading" options (as well as the per-script "allowDup" option1) were removed as they are no longer useful. This is the only backwards-incompatible change (no actual API changes, just config), and the change should just cause older usage code to continue to operate as normal while ignoring the no longer supported options. Still, test your code carefully if you've been using either of those 3 config options before.

1 This note about "allowDup" removal was ammended to the release notes on 6/21/2012, obviously long after the original release notes for 2.x were published. It was an accidental ommission, and was brought to light by this bug report.

1.2.0:

Browsers have long supported "protocol-relative URLs", which basically means leaving off the "http:" or "https:" portion of a URL (leaving just the "//domain.tld/path/…" part), which causes that URL to be assumed to be the same protocol as the parent page. The benefit is that if you have a page that can be viewed in either HTTP or HTTPS, and your resources can (and need to be) served through either HTTP or HTTPS, respectively, you can simply list your URLs as protocol-relative and the browser will auto-select based on which protocol the page is viewed in.

LABjs now supports specifying such URLs to any script URL setting. NOTE: This is the recommended way to specify URLs for script resources if: a) the page you're serving can be viewed in both HTTP and HTTPS; and b) the script resource you're linking to can be accessed using the exact same domain/path with exception to the protocol.

A common example of such a resource is the CDN locations on the Google Ajax API, where popular frameworks like jQuery and Dojo are hosted. If you are linking to such CDN resources, you are strongly encouraged to change to using protocol-relative URLs, like "//ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js" instead of "http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js" or "https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js".

1.1.12:

Fixed a minor (but long standing) bug around sections of the $LAB chain that are "empty" (that is, have nothing to load) not properly forwarding on the loading/execution cursor to the next section of the chain. This also corrects a failure in the previously released (in v1.1.11) functionality of conditional chaining with no-op functions in .script() calls.

1.1.11:

Adding one new feature: the ability to pass a function to a .script() call. If a function is found as one of the parameters evaluated by .script(), that function will be immediately executed, and the value that it returns will be used as the value for that parameter. This allows you to specify "conditional chains" where you use run-time conditional logic to decide which script should be loaded. See the Documentation for more information.

1.1.0-1.1.10:

These versions of LABjs were ghost versions that never saw the light of day. Or I just skipped them. I can't remember which.

1.0.4:

LABjs is broken in FF 4b7 related to changes in dynamic script execution ordering behavior. The new (informal) proposal to the W3C is to support `async=false` functionality, which FF 4b8pre took the lead in implementing. So this release implements a feature test for `async=false` and uses it.

1.0.3:

Fixed a bug where LABjs wasn't working in FF4 because of the removal of __count__ property which LABjs used as a Gecko inference. Also, cleaned up some code.

1.0.2rc1:

A number of tweaks and minor bug fixes. No more use of "rel" attributes on preload script tags. Changed the mimetype for preload script tags to "script/cache" (from "text/html"). Uses "insertBefore()" when adding scripts to the <head> to avoid possible bugs in IE6. Also, fixes use of live node lists to prevent bugs with IE where the list is invalidated after the DOM is changed.

1.0.1:

You knew there had to be an obligatory, right-after-release, oops-crap-i-just-found-a-bug, patch. This is that patch. Fixes a corner case error where if you happen to end your chain with just a script() statement and no wait() statement, the end of the chain is never detected (which causes the final script to never execute!).

1.0:

Final production release. 4.5k minified, 2.1k gzip'd. Have fun!

1.0rc5:

More optimizations for file-size. Also, changed XHR monitoring from setInterval() polling to a "onreadystatechange" handler.

1.0rc4:

Added a "hack" suggested by Andrea Giammarchi and adapted from: http://webreflection.blogspot.com/2009/11/195-chars-to-help-lazy-loading.html NOTE: this hack only operates in FF and then only in versions where document.readyState is not present (FF < 3.6?).

The hack essentially "patches" the page that LABjs is loaded onto so that it has a proper conforming document.readyState, so that if a script which does proper and safe dom-ready detection is loaded onto a page, after dom-ready has passed, it will still be able to detect this state, by inspecting the now hacked document.readyState property. The loaded script in question can then immediately trigger any queued code executions that were waiting for the DOM to be ready. For instance, jQuery > 1.3.2 has been patched to take advantage of document.readyState, which is enabled by this hack. But 1.3.2 and before are **not** safe or affected by this hack, and should therefore **not** be lazy-loaded by script loader tools such as LABjs.

1.0rc3:

More tweaks to reduce file-size and improve performance. Also, fLABjs is now included, which is a special API wrapper for usage of LABjs in file:// local filesystem environments. Source on github here.

1.0rc2:

Tweaks for performance, changes to browser sniffing "hacks".

1.0rc1:

Huge reworking, adding the new "preloading" feature, new API. Documentation Coming Soon!

0.9:

Further improvements to code, including smaller size (2.1k now!), and more efficient run-time execution.

About 智足者富

http://chenpeng.info

发表评论

电子邮件地址不会被公开。 必填项已用*标注

您可以使用这些HTML标签和属性:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>