ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScriptの読み込みと実行効率を高速化します

JavaScriptの読み込みと実行効率を高速化します

黄舟
黄舟オリジナル
2017-02-21 11:22:001200ブラウズ



ブラウザーでの JavaScript のパフォーマンスは、開発者が直面する最も重要なユーザビリティの問題となっています。この問題は、JavaScript のブロック的な性質によって複雑になります。つまり、ブラウザーが JavaScript コードを実行している間は、同時に他のことを行うことができません。この記事では、JavaScript コードを適切にロードして実行し、ブラウザーでのパフォーマンスを向上させる方法について詳しく説明します。

概要

現在の JavaScript コードが埋め込まれているか、外部リンク ファイルに埋め込まれているかに関係なく、ページのダウンロードとレンダリングは停止し、スクリプトの実行が完了するまで待つ必要があります。 JavaScript の実行プロセスに時間がかかるほど、ブラウザがユーザー入力に応答するまでの待ち時間も長くなります。スクリプトのダウンロードおよび実行時にブラウザがブロックされる理由は、スクリプトによってページまたは JavaScript の名前空間が変更され、後続のページのコンテンツに影響を与える可能性があるためです。典型的な例は、ページで document.write() を使用することです。

JavaScript コードのインライン例

<html>
<head>
    <title>Source Example</title>
</head>
<body>
    <p>
    <script type="text/javascript">
        document.write("Today is " + (new Date()).toDateString());
    </script>
    </p>
</body>
</html>

ブラウザーが 3f1c4e4b6b16bbbd69b2ee476dc4f83a タグに遭遇したとき、現在の HTML ページでは、JavaScript が e388a4556c0f65e1904146cc1a846bee タグにコンテンツを追加するか、他の要素を導入するか、さらには削除するかを知る方法がありません。鬼ごっこ 。したがって、ブラウザーはこの時点でページの処理を停止し、最初に JavaScript コードを実行してから、ページの解析とレンダリングを続行します。 src 属性を使用して JavaScript をロードするときにも、同じ状況が発生します。ブラウザは、まず外部リンク ファイル内のコードをダウンロードし、それからそれを解析して実行するのに時間を費やす必要があります。このプロセス中、ページのレンダリングとユーザーの操作は完全にブロックされます。

スクリプトの配置

HTML 4 仕様では、3f1c4e4b6b16bbbd69b2ee476dc4f83a タグを HTML ドキュメントの 93f0f5c25f18dab9d176bd4f6de5d30e または 6c04bd5ca3fcae76e30b72ad730ca86d に配置でき、複数回出現することが許可されています。 Web 開発者は通常、外部リンクの JavaScript を 93f0f5c25f18dab9d176bd4f6de5d30e にロードし、次に 2cdf5bf648cf2f33323966d7f58a7f3f タグを使用して外部リンクの CSS ファイルまたはその他のページ情報をロードすることに慣れています。

非効率なスクリプトの場所の例

<html>
<head>
    <title>Source Example</title>
    <script type="text/javascript" src="script1.js"></script>
    <script type="text/javascript" src="script2.js"></script>
    <script type="text/javascript" src="script3.js"></script>
    <link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
    <p>Hello world!</p>
</body>
</html>

ただし、この一般的なアプローチには、重大なパフォーマンスの問題が隠れています。リスト 2 の例では、ブラウザーが 3f1c4e4b6b16bbbd69b2ee476dc4f83a タグ (行 4) を解析すると、ブラウザーはその後のコンテンツの解析を停止し、最初にスクリプト ファイルをダウンロードしてその中のコードを実行します。つまり、後続のスタイルは.css スタイル ファイルと 6c04bd5ca3fcae76e30b72ad730ca86d タグを読み込むことができないため、ページを表示できません。したがって、JavaScript コードが完全に実行されるまで、ページは空白になります。

スクリプトはページ上の他のリソースのダウンロードをブロックするため、すべての 3f1c4e4b6b16bbbd69b2ee476dc4f83a タグをできるだけ 6c04bd5ca3fcae76e30b72ad730ca86d タグの下部に配置することをお勧めします。ページ。

推奨されるコードの配置例

<html>
<head>
    <title>Source Example</title>
    <link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
    <p>Hello world!</p>

    <!-- Example of efficient script positioning -->
    <script type="text/javascript" src="script1.js"></script>
    <script type="text/javascript" src="script2.js"></script>
    <script type="text/javascript" src="script3.js"></script>
</body>
</html>

このコードは、HTML ドキュメント内の 3f1c4e4b6b16bbbd69b2ee476dc4f83a タグの推奨される配置を示しています。スクリプトのダウンロードによって別のスクリプトがブロックされても、ページのコンテンツのほとんどはすでにダウンロードされてユーザーに表示されているため、ページのダウンロードがそれほど遅いようには見えません。これは JavaScript を最適化するための最初のルールです。スクリプトを最後に配置します。

スクリプトを整理する

最初にダウンロードされるときに各 3f1c4e4b6b16bbbd69b2ee476dc4f83a タグがページのレンダリングをブロックするため、ページに含まれる 3f1c4e4b6b16bbbd69b2ee476dc4f83a タグの数を減らすと、この状況を改善できます。これは、外部リンク スクリプトだけでなく、埋め込みスクリプトの数にも当てはまります。ブラウザが HTML ページの解析プロセス中に <script> タグに遭遇するたびに、スクリプトの実行により一定の遅延が発生するため、遅延時間を最小限に抑えると、ページの全体的なパフォーマンスが大幅に向上します。 </script>

外部リンクの JavaScript ファイルを扱う場合、この問題は少し異なります。 HTTP リクエストに関連する追加のパフォーマンス オーバーヘッドを考慮すると、1 つの 100 Kb ファイルをダウンロードする方が、5 つの 20 Kb ファイルをダウンロードするよりも高速になります。ただし、ページ上の外部スクリプトの数を減らすとパフォーマンスが向上します。

通常、大規模な Web サイトやアプリケーションは複数の JavaScript ファイルに依存しています。複数のファイルを 1 つにマージできるため、参照する必要があるのは 3f1c4e4b6b16bbbd69b2ee476dc4f83a タグのみとなり、パフォーマンスの消費を削減できます。ファイルのマージは、オフライン パッケージング ツールまたは一部のリアルタイム オンライン サービスを通じて実行できます。

外部スタイル シートを参照する 2cdf5bf648cf2f33323966d7f58a7f3f の後にインライン スクリプトを配置すると、スタイル シートがダウンロードされるまでページがブロックされることに注意してください。これは、インライン スクリプトが実行時に最も正確なスタイル情報を取得できるようにするために行われます。したがって、2cdf5bf648cf2f33323966d7f58a7f3f タグの直後にインライン スクリプトを配置しないことをお勧めします。

ノンブロッキング スクリプト

JavaScript ファイル サイズを削減し、HTTP リクエストの数を制限することは、機能が豊富な Web アプリケーションや大規模な Web サイトでは常に実現できるとは限りません。 Web アプリケーションの機能が豊富であればあるほど、より多くの JavaScript コードが必要になります。また、より大きな JavaScript ファイルを 1 つダウンロードしても HTTP リクエストが 1 つしか生成されませんが、ブラウザーが長時間ロックされる可能性があります。これを回避するには、ブラウザをブロックしない方法で JavaScript ファイルをページに徐々に読み込む特定のテクニックが必要です。

无阻塞脚本的秘诀在于,在页面加载完成后才加载 JavaScript 代码。这就意味着在 window 对象的 onload 事件触发后再下载脚本。有多种方式可以实现这一效果。

延迟加载脚本

HTML 4 为 3f1c4e4b6b16bbbd69b2ee476dc4f83a 标签定义了一个扩展属性: defer 。 Defer 属性指明本元素所含的脚本不会修改 DOM,因此代码能安全地延迟执行。 defer 属性只被 IE 4 和 Firefox 3.5 更高版本的浏览器所支持,所以它不是一个理想的跨浏览器解决方案。在其他浏览器中, defer 属性会被直接忽略,因此 3f1c4e4b6b16bbbd69b2ee476dc4f83a 标签会以默认的方式处理,也就是说会造成阻塞。然而,如果您的目标浏览器支持的话,这仍然是个有用的解决方案。

defer 属性使用方法示例

<script type="text/javascript" src="script1.js" defer></script>

带有 defer 属性的 3f1c4e4b6b16bbbd69b2ee476dc4f83a 标签可以放置在文档的任何位置。对应的 JavaScript 文件将在页面解析到 3f1c4e4b6b16bbbd69b2ee476dc4f83a 标签时开始下载,但不会执行,直到 DOM 加载完成,即 onload 事件触发前才会被执行。当一个带有 defer 属性的 JavaScript 文件下载时,它不会阻塞浏览器的其他进程,因此这类文件可以与其他资源文件一起并行下载。

任何带有 defer 属性的 3f1c4e4b6b16bbbd69b2ee476dc4f83a 元素在 DOM 完成加载之前都不会被执行,无论内嵌或者是外链脚本都是如此。清单 5 的例子展示了 defer 属性如何影响脚本行为:

defer 属性对脚本行为的影响

<html>
<head>
    <title>Script Defer Example</title>
</head>
<body>
    <script type="text/javascript" defer>
        alert("defer");
    </script>
    <script type="text/javascript">
        alert("script");
    </script>
    <script type="text/javascript">
        window.onload = function(){
            alert("load");
        };
    </script>
</body>
</html>

这段代码在页面处理过程中弹出三次对话框。不支持 defer 属性的浏览器的弹出顺序是:“defer”、“script”、“load”。而在支持 defer 属性的浏览器上,弹出的顺序则是:“script”、“defer”、“load”。请注意,带有 defer 属性的 3f1c4e4b6b16bbbd69b2ee476dc4f83a 元素不是跟在第二个后面执行,而是在 onload 事件被触发前被调用。

如果您的目标浏览器只包括 Internet Explorer 和 Firefox 3.5,那么 defer 脚本确实有用。如果您需要支持跨领域的多种浏览器,那么还有更一致的实现方式。

HTML 5 为 3f1c4e4b6b16bbbd69b2ee476dc4f83a 标签定义了一个新的扩展属性: async 。它的作用和 defer 一样,能够异步地加载和执行脚本,不因为加载脚本而阻塞页面的加载。但是有一点需要注意,在有 async 的情况下,JavaScript 脚本一旦下载好了就会执行,所以很有可能不是按照原本的顺序来执行的。如果 JavaScript 脚本前后有依赖性,使用 async 就很有可能出现错误。

动态脚本元素

文档对象模型(DOM)允许您使用 JavaScript 动态创建 HTML 的几乎全部文档内容。 3f1c4e4b6b16bbbd69b2ee476dc4f83a 元素与页面其他元素一样,可以非常容易地通过标准 DOM 函数创建:

通过标准 DOM 函数创建3f1c4e4b6b16bbbd69b2ee476dc4f83a元素

var script = document.createElement ("script");
   script.type = "text/javascript";
   script.src = "script1.js";
   document.getElementsByTagName("head")[0].appendChild(script);

新的 3f1c4e4b6b16bbbd69b2ee476dc4f83a 元素加载 script1.js 源文件。此文件当元素添加到页面之后立刻开始下载。此技术的重点在于:无论在何处启动下载,文件的下载和运行都不会阻塞其他页面处理过程。您甚至可以将这些代码放在 93f0f5c25f18dab9d176bd4f6de5d30e 部分而不会对其余部分的页面代码造成影响(除了用于下载文件的 HTTP 连接)。

当文件使用动态脚本节点下载时,返回的代码通常立即执行(除了 Firefox 和 Opera,他们将等待此前的所有动态脚本节点执行完毕)。当脚本是“自运行”类型时,这一机制运行正常,但是如果脚本只包含供页面其他脚本调用调用的接口,则会带来问题。这种情况下,您需要跟踪脚本下载完成并是否准备妥善。可以使用动态 3f1c4e4b6b16bbbd69b2ee476dc4f83a 节点发出事件得到相关信息。

Firefox、Opera, Chorme 和 Safari 3+会在 3f1c4e4b6b16bbbd69b2ee476dc4f83a 节点接收完成之后发出一个 onload 事件。您可以监听这一事件,以得到脚本准备好的通知:

通过监听 onload 事件加载 JavaScript 脚本

var script = document.createElement ("script")
script.type = "text/javascript";

//Firefox, Opera, Chrome, Safari 3+
script.onload = function(){
    alert("Script loaded!");
};

script.src = "script1.js";
document.getElementsByTagName("head")[0].appendChild(script);

Internet Explorer 支持另一种实现方式,它发出一个 readystatechange 事件。 3f1c4e4b6b16bbbd69b2ee476dc4f83a 元素有一个 readyState 属性,它的值随着下载外部文件的过程而改变。 readyState 有五种取值:

微软文档上说,在 3f1c4e4b6b16bbbd69b2ee476dc4f83a 元素的生命周期中, readyState 的这些取值不一定全部出现,但并没有指出哪些取值总会被用到。实践中,我们最感兴趣的是“loaded”和“complete”状态。Internet Explorer 对这两个 readyState 值所表示的最终状态并不一致,有时 3f1c4e4b6b16bbbd69b2ee476dc4f83a 元素会得到“loader”却从不出现“complete”,但另外一些情况下出现“complete”而用不到“loaded”。最安全的办法就是在 readystatechange 事件中检查这两种状态,并且当其中一种状态出现时,删除 readystatechange 事件句柄(保证事件不会被处理两次):

通过检查 readyState 状态加载 JavaScript 脚本

var script = document.createElement("script")
script.type = "text/javascript";

//Internet Explorer
script.onreadystatechange = function(){
     if (script.readyState == "loaded" || script.readyState == "complete"){
           script.onreadystatechange = null;
           alert("Script loaded.");
     }
};

script.src = "script1.js";
document.getElementsByTagName("head")[0].appendChild(script);

大多数情况下,您希望调用一个函数就可以实现 JavaScript 文件的动态加载。下面的函数封装了标准实现和 IE 实现所需的功能:

通过函数进行封装

function 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);
}

此函数接收两个参数:JavaScript 文件的 URL,和一个当 JavaScript 接收完成时触发的回调函数。属性检查用于决定监视哪种事件。最后一步,设置 src 属性,并将 3f1c4e4b6b16bbbd69b2ee476dc4f83a 元素添加至页面。此 loadScript() 函数使用方法如下:

loadScript()函数使用方法

loadScript("script1.js", function(){
    alert("File is loaded!");
});

您可以在页面中动态加载很多 JavaScript 文件,但要注意,浏览器不保证文件加载的顺序。所有主流浏览器之中,只有 Firefox 和 Opera 保证脚本按照您指定的顺序执行。其他浏览器将按照服务器返回它们的次序下载并运行不同的代码文件。您可以将下载操作串联在一起以保证他们的次序,如下:

通过 loadScript()函数加载多个 JavaScript 脚本

loadScript("script1.js", function(){
    loadScript("script2.js", function(){
        loadScript("script3.js", function(){
            alert("All files are loaded!");
        });
    });
});

此代码等待 script1.js 可用之后才开始加载 script2.js,等 script2.js 可用之后才开始加载 script3.js。虽然此方法可行,但如果要下载和执行的文件很多,还是有些麻烦。如果多个文件的次序十分重要,更好的办法是将这些文件按照正确的次序连接成一个文件。独立文件可以一次性下载所有代码(由于这是异步进行的,使用一个大文件并没有什么损失)。

动态脚本加载是非阻塞 JavaScript 下载中最常用的模式,因为它可以跨浏览器,而且简单易用。

使用 XMLHttpRequest(XHR)对象

此技术首先创建一个 XHR 对象,然后下载 JavaScript 文件,接着用一个动态 3f1c4e4b6b16bbbd69b2ee476dc4f83a 元素将 JavaScript 代码注入页面。清单 12 是一个简单的例子:

通过 XHR 对象加载 JavaScript 脚本

var xhr = new XMLHttpRequest();
xhr.open("get", "script1.js", true);
xhr.onreadystatechange = function(){
    if (xhr.readyState == 4){
        if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
            var script = document.createElement ("script");
            script.type = "text/javascript";
            script.text = xhr.responseText;
            document.body.appendChild(script);
        }
    }
};
xhr.send(null);

此代码向服务器发送一个获取 script1.js 文件的 GET 请求。 onreadystatechange 事件处理函数检查 readyState 是不是 4,然后检查 HTTP 状态码是不是有效(2XX 表示有效的回应,304 表示一个缓存响应)。如果收到了一个有效的响应,那么就创建一个新的 3f1c4e4b6b16bbbd69b2ee476dc4f83a 元素,将它的文本属性设置为从服务器接收到的 responseText 字符串。这样做实际上会创建一个带有内联代码的 3f1c4e4b6b16bbbd69b2ee476dc4f83a 元素。一旦新 3f1c4e4b6b16bbbd69b2ee476dc4f83a 元素被添加到文档,代码将被执行,并准备使用。

这种方法的主要优点是,您可以下载不立即执行的 JavaScript 代码。由于代码返回在 3f1c4e4b6b16bbbd69b2ee476dc4f83a 标签之外(换句话说不受 3f1c4e4b6b16bbbd69b2ee476dc4f83a 标签约束),它下载后不会自动执行,这使得您可以推迟执行,直到一切都准备好了。另一个优点是,同样的代码在所有现代浏览器中都不会引发异常。

此方法最主要的限制是:JavaScript 文件必须与页面放置在同一个域内,不能从 CDN 下载(CDN 指"内容投递网络(Content Delivery Network)",所以大型网页通常不采用 XHR 脚本注入技术。

总结

减少 JavaScript 对性能的影响有以下几种方法:

通过以上策略,可以在很大程度上提高那些需要使用大量 JavaScript 的 Web 网站和应用的实际性能。

原文来自:http://www.php.cn/

补充js加载函数:

function loadJs(url, callback, charset) {
    var head = document.getElementsByTagName("head")[0];
    var script = document.createElement("script");
    if ( !!charset) script.charset = "utf-8";
    script.src = url;
    script.onload = script.onreadystatechange = function() {
        var f = script.readyState;
        if (f && f != "loaded" && f != "complete") return;
        script.onload = script.onreadystatechange = null;
        head.removeChild(script) if (callback) {
            callback() || callback
        };
    };
    head.appendChild(script);
}
// js同步加载
function getScripts(i, linkArray, fn) {
    env || getEnv();
    var script = document.createElement(&#39;script&#39;);
    script.type = &#39;text/javascript&#39;;
    script.src = linkArray[i];
    var head = document.head || document.getElementsByTagName(&#39;head&#39;)[0];
    head.appendChild(script);

    if (env.ie && &#39;onreadystatechange&#39; in script && !(&#39;draggable&#39; in script)){ //ie浏览器使用以下方式加载
        script.onreadystatechange = function () {
          if (/loaded|complete/.test(script.readyState)) {
            script.onreadystatechange = null;
            if(i === linkArray.length-1) {
                if (fn) {
                    fn();
                }
            } else {
                getScripts(++i, linkArray, fn);
            }
          }
        };
    }else{
        script.onload = function() {
            if(i === linkArray.length-1) {
                if (fn) {
                    fn();
                }
            } else {
                getScripts(++i, linkArray, fn);
            }
        };
    }
}
// js存在依赖关系 依次加载
getScripts(0, [
    &#39;http://caibaojian.com/demo/base.js&#39;,
    &#39;http://caibaojian.com/demo/reset.js&#39;], function() {
     alert(&#39;callback&#39;);
});

以上就是加快JavaScript加载和执行效率的内容,更多相关内容请关注PHP中文网(www.php.cn)!

 

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。