However, this conventional approach hides serious performance problems. In the example in Listing 2, when the browser resolves to the <script>
label (line 4th), the browser stops parsing the content, and the script file is first downloaded, and the code is executed, which means that the Styles.css style files and <body>
tags after it cannot be loaded because <body>
The label cannot be loaded, so the page will not be rendered naturally. So before the JavaScript code is fully executed, the page is blank. Figure 1 describes the download process for script and style files during page loading.
Figure 1 The loading and execution of JavaScript files blocks the download of other filesWe can find an interesting phenomenon: the first JavaScript file starts to download, and at the same time blocks the download of other files on the page. In addition, there is a delay from the completion of the Script1.js download to the beginning of the Script2.js download, which is exactly the Script1.js file execution process. Each file must wait until the previous file is downloaded and executed before the download begins. In the process of downloading these files individually, the user sees a blank page.
Starting with IE 8, Firefox 3.5, Safari 4 and Chrome 2, JavaScript files are allowed to be downloaded in parallel. This is good news because <script>
the label does not block other tags when it downloads external resources <script>
. Unfortunately, the JavaScript download process still blocks downloads of other resources, such as style files and pictures. Although the download process for scripts does not affect each other, the page must still wait for all JavaScript code to download and complete before continuing. Therefore, although the latest browser improves performance by allowing parallel downloads, the problem is not yet fully resolved, and script blocking remains a problem.
Because the script blocks the download of other resources on the page, it is recommended that all tags be placed at the bottom of the label as much as possible to minimize the <script>
<body>
impact on the entire page download. For example, listing 3
Listing 3 recommended Code placement examplesThis code shows the recommended location for placing labels in an HTML document <script>
. Although a script download blocks another script, most of the page's content is downloaded and displayed to the user, so the page download does not appear too slow. This is the first rule to optimize JavaScript: Put the script at the bottom.
Back to top of page
Organizing scriptsBecause <script>
page rendering is blocked at the initial download of each label, reducing the number of labels included in the page <script>
can help improve the situation. This is not only for outer-chain scripts, but also for the number of inline scripts to be limited. Each time a browser encounters a tag during parsing of an HTML page, a <script>
delay is caused by the execution of the script, so minimizing the latency will significantly improve the overall performance of the page.
This problem is slightly different when dealing with an outside-chain JavaScript file. Given the additional performance overhead associated with HTTP requests, downloading a single 100Kb file will be faster than downloading 5 20Kb files. In other words, reducing the number of scripts on the page will improve performance.
Typically a large web site or application relies on several JavaScript files. You can combine multiple files into one, so you <script>
can reduce performance consumption by simply referencing a label. The work of file merging can be done through offline packaging tools or some real-time online services.
A special reminder is that placing an inline script after referencing an outer-chain style sheet <link>
causes the page to block to wait for the stylesheet to be downloaded. This is done to ensure that the inline script obtains the most accurate style information when it executes. Therefore, it is recommended that you do not immediately follow the tag in the inline script <link>
.
Back to top of page
Non-blocking scriptsReducing the size of JavaScript files and limiting the number of HTTP requests is not always feasible on a feature-rich Web application or large Web site. The richer the Web app is, the more JavaScript code is needed, although downloading a single large JavaScript file produces only one HTTP request, which locks the browser for a long time. To avoid this, it is necessary to incrementally load JavaScript files into the page through some specific techniques that do not block the browser in some way.
The secret of a nonblocking script is that the JavaScript code is loaded after the page is loaded. This means that the window
script is downloaded after the object's onload
event is triggered. There are several ways to achieve this effect.
Deferred load scriptHTML 4 <script>
defines an extended property for the tag: defer
. Defer
property indicates that the script contained in this element does not modify the DOM, so the code can safely defer execution. defer
properties are supported only by browsers of IE 4 and Firefox 3.5 later, so it is not an ideal cross-browser solution. In other browsers, defer
attributes are ignored directly, so the <script>
labels are handled in the default way, which in other words can cause blocking. However, this is still a useful solution if your target browser supports it. Listing 4 is an example
Listing 4 Defer properties using the method Example<script type= "Text/javascript" src= "Script1.js" defer></script>
defer
labels with attributes <script>
can be placed anywhere in the document. The corresponding JavaScript file will start downloading when the page resolves to the <script>
label, but will not execute until the DOM is loaded, that is, onload
before the event is triggered. When a defer
JavaScript file with attributes is downloaded, it does not block other browsers ' processes, so such files can be downloaded in parallel with other resource files.
Any defer
element with attributes <script>
will not be executed until the DOM has finished loading, either inline or outside the chain script. The example in Listing 5 shows defer
how the properties affect script behavior:
Listing 5 Effects of the Defer property on script behaviorThis code pops up three dialog boxes during page processing. Browsers that do not support defer
properties have the following popup order: "Defer", "script", "load". defer
on browsers that support properties, the order of popup is "script", "defer", and "load". Note that the defer
element with the attribute is not followed by the <script>
second, but onload
is called before the event is triggered.
If your target browser includes only Internet Explorer and Firefox 3.5, then the defer
script is really useful. If you need to support multiple browsers across domains, there is a more consistent way to implement them.
HTML 5 <script>
defines a new extended property for the tag: async
. It works the defer
same way that scripts can be loaded and executed asynchronously, without blocking the loading of the page because of the load script. It is important to note, however, that async
JavaScript scripts are executed once they have been downloaded, so it is likely that they are not executed in the original order. If a JavaScript script has dependencies before and after it, there is a async
good chance that an error will occur.
Dynamic scripting elementsThe Document Object Model (DOM) allows you to dynamically create almost all of the document content of HTML using JavaScript. <script>
elements, like other elements of a page, can be easily created with standard DOM functions:
Listing 6 creating <script> elements with standard DOM functionsvar script = document.createelement ("script"); Script.type = "Text/javascript"; SCRIPT.SRC = "Script1.js"; document.getElementsByTagName ("Head") [0].appendchild (script);
The new <script>
element loads the script1.js source file. This file starts downloading immediately after the element is added to the page. The point of this technique is that no matter where the download is initiated, the download and operation of the file does not block other page processing processes. You can even place the code in part without affecting the rest of the page code (except for the HTTP connection used to download the file).
When a file is downloaded using a dynamic script node, the returned code is usually executed immediately (except for Firefox and Opera, they will wait until all the previous dynamic script nodes have been executed). This mechanism works fine when the script is a "self-running" type, but it can cause problems if the script contains only the interfaces that are called by other script calls on the page. In this case, you need to track the script download complete and be prepared properly. Events can be <script>
emitted using dynamic nodes to get information about them.
Firefox, Opera, Chorme, and Safari will <script>
issue an event after the node is received onload
. You can listen to this event to get the script ready for notification:
Listing 7 loading JavaScript scripts by listening for onload eventsvar script = document.createelement ("script") Script.type = "Text/javascript";//firefox, Opera, Chrome, Safari Script.onload = function () { alert ("Script loaded!");}; SCRIPT.SRC = "Script1.js";d ocument.getelementsbytagname ("Head") [0].appendchild (script);
Internet Explorer supports an alternative implementation, which emits an readystatechange
event. <script>
element has an readyState
attribute whose value changes as the process of downloading an external file. readyState
There are five kinds of values:
- "Uninitialized": Default state
- "Loading": Download start
- "Loaded": Download complete
- "Interactive": Download complete but not yet available
- "Complete": all data is ready.
In the Microsoft documentation, <script>
these values do not necessarily appear in the lifetime of the element, readyState
but do not indicate which values are always used. In practice, we are most interested in the "loaded" and "complete" states. Internet Explorer readyState
does not agree on the final state of these two values, sometimes the <script>
element gets "loader" but never "complete", but in other cases "complete" is not used to "loaded". The safest way to readystatechange
do this is to check both states in an event, and when one of these states is present, delete the readystatechange
event handle (which guarantees that the event will not be processed two times):
Listing 8 loading JavaScript scripts by checking the ReadyState statevar script = document.createelement ("script") Script.type = "Text/javascript";//internet Explorerscript.onreadystatechange = function () { if (script.readystate = = "Loaded" | | script.readystate = = "complete ") { script.onreadystatechange = null; Alert ("Script loaded.");} ; SCRIPT.SRC = "Script1.js";d ocument.getelementsbytagname ("Head") [0].appendchild (script);
In most cases, you want to invoke a function to implement the dynamic loading of a JavaScript file. The following function encapsulates the functionality required by the standard implementation and IE implementations:
Listing 9 Encapsulation by functionfunction Loadscript (URL, callback) { var script = document.createelement ("script") Script.type = "text/ JavaScript "; if (script.readystate) {//ie script.onreadystatechange = function () { if (script.readystate = = "Loaded" | | Script.readystate = = "complete") { script.onreadystatechange = null; Callback (); } }; } else {//others script.onload = function () { callback ();} ; } script.src = URL; document.getElementsByTagName ("Head") [0].appendchild (script);}
This function receives two parameters: The URL of the JavaScript file, and a callback function that fires when the JavaScript receive is complete. The property check is used to determine which event to monitor. The final step is to set the src
properties and <script>
add the elements to the page. This loadScript()
function is used in the following ways:
List of Loadscript () functions using the methodLoadscript ("Script1.js", function () { alert ("File is loaded!");});
You can load many JavaScript files dynamically in the page, but be aware that the browser does not guarantee the order in which the files are loaded. Of all the major browsers, only Firefox and Opera guarantee that the scripts are executed in the order you specify. Other browsers will download and run different code files in the order in which they are returned by the server. You can concatenate the download operations together to ensure their order, as follows:
Listing 11 loading multiple JavaScript scripts with the Loadscript () functionLoadscript ("Script1.js", function () { loadscript ("Script2.js", function () { loadscript ("Script3.js", function () { alert ("All Files is loaded!");});});
This code waits until Script1.js is available to start loading script2.js, and so script2.js starts loading script3.js after it is available. While this approach works, it's still a bit cumbersome to download and execute a lot of files. If the order of multiple files is important, a better way is to connect the files to a file in the correct order. Standalone files can download all the code at once (since this is done asynchronously, there is no loss in using a large file).
Dynamic scripting loads the most common pattern in non-blocking JavaScript downloads, because it can be cross-browser and easy to use.
Using XMLHttpRequest (XHR) objectsThis technique first creates a XHR object, then downloads the JavaScript file, and then <script>
injects the JavaScript code into the page with a dynamic element. Listing 12 is a simple example:
Listing 12 loading JavaScript scripts with XHR objectsvar xhr = new XMLHttpRequest (), Xhr.open ("Get", "Script1.js", true); Xhr.onreadystatechange = function () { if ( Xhr.readystate = = 4) { if (xhr.status >= && Xhr.status < | | xhr.status = 304) { var script = Document.createelement ("script"); Script.type = "Text/javascript"; Script.text = Xhr.responsetext; Document.body.appendChild (script);}} ; Xhr.send (NULL);
This code sends a GET request to the server to get the Script1.js file. onreadystatechange
the event handler check readyState
is not 4, and then check that the HTTP status code is not valid (2XX indicates a valid response, and 304 represents a cached response). If a valid response is received, a new element is created <script>
and its Text property is set to the string received from the server responseText
. Doing so will actually create an element with inline code <script>
. Once the new <script>
element is added to the document, the code is executed and ready to be used.
The main advantage of this approach is that you can download JavaScript code that is not executed immediately. Since the code is returned <script>
outside the label (in other words, not <script>
tagged), it is not automatically executed after it is downloaded, which allows you to defer execution until everything is ready. Another advantage is that the same code does not throw an exception in all modern browsers.
The main limitation of this method is that the JavaScript file must be placed in the same domain as the page and cannot be downloaded from the CDN (Cdn refers to the content Delivery network), so a large web page usually does not use XHR script injection technology.
Performance optimizations for JavaScript: loading and executing