
























Each approach solves a very specific problem but no approach solves them all. For instance, there is no way to download a JavaScript file and delay it’s execution until a later point in time that can be defined by the developer (deferred scripts are the closest, but the execution point is predetermined and unchangeable).
This proposal is intended to describe a mechanism whereby JavaScript can be included in an HTML page, either via inline script or external file, and the parsing and execution is delayed until a time determined by the developer.
Currently, there are various script loaders such as LAB.js and ControlJS that seek to alter the way that scripts are loaded onto a page. Each script loader operates on their own theory of how best to load script files, whether to optimize for parallel downloads or delay execution until a later point in time. After some consideration, it seems that developer-controlled execution time of JavaScript is a minimal feature that would enable parallel downloads as well as other execution paradigms.
Allow scripts to be marked as “preload”, indicating that the script should be downloaded (in the case of external scripts) but not executed immediately. Since the script isn’t being executed, it must not block other page resources from being downloaded (which would allow parallel downloading). Developers may instruct the script to execute at a later point in time.
This proposal is based largely on the Internet Explorer implementation for dynamic script nodes. In that implementation, the external script file begins to download as soon as src is assigned but the script is not executed until the node is added to the document. This is a nice feature but suffers from several drawbacks:
This proposal seeks to build upon this behavior to fix these shortcomings.
There is no HTML usage for the preload attribute. If an attribute named preload is included in an HTML <script> tag, it must be treated as an unknown attribute.
Scripts may be dynamically loaded from JavaScript by creating a <script> element and setting its preload property to true (default value is false). Example:
var script = document.createElement(“script”); script.preload = true; script.src = “foo.js”;
When the src attribute is assigned, the JavaScript file should be downloaded per user agent default and stored in the appropriate user agent cache(s) but not executed. User agents may background parse or compile the script in preparation for execution but must not execute the code until instructed to do so.
In order to execute the code, the script node must be added to the document:
document.body.appendChild(script);
Upon adding into the document, the contents of the external file referenced by the <script> element must be executed synchronously in the global scope per usual user agent behavior.
In order to accurately detect and react to changes in script state due to this proposal, several changes and additions are necessary to the HTMLScriptElement.
interface HTMLScriptElement : HTMLElement {
attribute DOMString src;
attribute boolean async;
attribute boolean defer;
attribute DOMString type;
attribute DOMString charset;
attribute DOMString text;
attribute boolean preload;
};
The only change is the introduction of a preload property. As discussed earlier, setting this to true prior to the src being set puts the script into preload mode, where code may be downloaded but not executed.
The default value for preload is false. When preload is false, the user agent may download and execute the external script according to its normal behavior.
For feature detection, a developer may determine if this proposal has been implemented by detecting the preload property:
var preloadSupported = (typeof document.createElement(“script”).preload == “boolean”);Changes to HTMLScriptElement Events
HTML5 defines two events: load and error. The load event fires on preload scripts only after the script has been downloaded and executed; the error event functions exactly the same for preload scripts.
Additionally, In order to fulfill the requirements of this proposal, it’s necessary to introduce a new event for HTMLScriptElement. The preload event fires with a preload script has been completely loaded and is ready for execution. You can therefore determine when a script has been preloaded using the following:
var script = document.createElement(“script”); script.preload = true; script.src = “foo.js”; script.onpreload = function(event){ document.head.appendChild(script); };
To ensure a script has been preloaded properly, you should also listen for the error event.
Since preloaded scripts alter the behavior of dynamic scripts slightly, there are a few edge cases to consider.
One possible augmentation of this proposal is that preload automatically be set to true for script elements created using JavaScript, thus allowing developers to opt-out of this functionality by setting to false rather than opt-in. This likely would work since this is the default behavior in Internet Explorer already.
The following is an example of using this proposal’s functionality to simulate deferred scripts in JavaScript. Note that real deferred scripts fire before DOMContentLoaded, while this will fire during.
(function(){ var domLoaded = false, script = document.createElement(“script”); script.preload = true; script.onpreload = function(){ if(domLoaded){ //just in case it’s too late document.body.appendChild(script); } else { script._ready = true; } }; script.src = “foo.js”; document.addEventListener(“DOMContentLoaded”, function(){ if (script._ready){ document.body.appendChild(script); } domLoaded = true; }, false); })();Script Loader with Parallelization
The following is an example of a script loader that allows scripts to download in parallel but execute in the order in which they are specified. This is done by creating preload scripts for each external JavaScript file and waiting until they are all loaded. Once all are loaded, a loop is used to execute each in order.
var ScriptLoader = (function(){ var batches = []; function handlePreload(batchId, scriptId){ var batch = batches[batchId], script = batch.scripts[scriptId]; batch.loaded++; //all scripts are now loaded if (batch.loaded == batch.scripts.length){ for (var i=0, len=batch.scripts.length; i<len; i++){ document.body.appendChild(batch.scripts[i].scriptNode); } batch.success(); } } function handleError(batchId, scriptId){ var batch = batches[batchId]; batch.error() } return { loadScripts: function(urls, successCallback, errorCallback){ var batch = { scripts: [], success: successCallback, error: errorCallback, id: batches.length, loaded: 0 }, i=0, len = urls.length, script; batches.push(batch); while(i < len){ script = document.createElement("script"); script.type = "text/javascript"; script.preload = true; script.onpreload = (function(scriptId){ return function(){ handlePreload(batch.id, scriptId); }; })(i); script.onerror = (function(scriptId){ return function(){ handleError(batch.id, scriptId); }; })(i); batch.scripts.push({scriptNode: script, id: i}); script.src = urls[i]; document.head.appendChild(script); i++; } } }; })(); //usage ScriptLoader.loadScripts([“foo.js”, “bar.js”], function(){ foo.init(); }, function(){ alert(“On no!”); } );
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。