今回は、JavaScriptの非同期読み込みについて詳しく説明し、JavaScriptの非同期読み込みを扱う際の注意事項について、実際のケースを見てみましょう。
同期読み込みの問題
ここでの「読み込み」は、「ダウンロード」ではなく、解析と実行として理解できます。コード これらはすべてウォーターフォール読み込みであり、ブロックしていませんが、js の実行は常にブロックされます。これはどのような問題を引き起こしますか?インデックス ページで js をロードする必要があるが、リクエストの 1 つが長時間応答を受け取らない場合、後続の js コードの実行 (同期ロード) がブロックされ、ページのレンダリングを続行できなくなります (js の導入が完了した場合)はラベルの後の head にあります)。
<script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js'></script> <script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script> this is a test
たとえば、上記のコードは、index.html ファイルとして保存されますが、ページの本体は単純な string ですが、コードが実行された後もページは空白のままです。リクエストされた js が長時間ロードできないため (Google がブロックされているなどの理由で)、後続のコードの実行がブロックされ、ページをレンダリングできません。おそらく、js コードを
の前に置くと、ページが最初にレンダリングされると提案するかもしれません。良い方法です。js を後ろに配置してみます:
this is a test <script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js'></script> <script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
ページは即座にレンダリングされ、「これはテストです」もすぐに前景に表示され、世界は穏やかに見えますが、:
this is a test <script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js'></script> <script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
単に追加するだけです前のコードの基礎 コードを書きましたが、明らかに、前の js リクエストが次のコードの読み込みをブロックしていたことに気づきました。 js の読み込み位置はページのレンダリングを変更するだけですが、js の読み込みは変更されません。js は依然としてブロックされます。
私たちの要件は非常に単純であるように思えます。より簡単に言うと、Google が提供する最初の js をリクエストしながら、次のコードを実行し続けることができます。 js は、js の非同期読み込みを実装します。
最も一般的な方法は、スクリプト タグを動的に生成することです:
<body> this is a test <script type="text/javascript"> ~function() { var s = document.createElement('script'); s.src = 'http://china-addthis.googlecode.com/svn/trunk/addthis.js'; document.body.appendChild(s); }(); </script> <script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script> <script type="text/javascript"> console.log('hello world'); </script> </body>
しかし、この読み込み方法では、読み込みが完了する前に onload イベントがトリガーされなくなり、多くのページで onload 中に追加のコードを実行する必要があります。レンダリング作業などがあるため、一部のページの初期化処理がブロックされます:
<body> this is a test <script type="text/javascript"> ~function() { // function async_load() { var s = document.createElement('script'); s.src = 'http://china-addthis.googlecode.com/svn/trunk/addthis.js'; document.body.appendChild(s); // } // window.addEventListener('load', async_load, false); }(); window.onload = function() { var txt = document.createTextNode(' hello world'); document.body.appendChild(txt); }; </script> <script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script> </body>
たとえば、上記のコードでは「hello world」を適切にレンダリングできません。コメントを削除して、Google が提供する JS を実行するだけで済みます。 onload 時に使用されます。 非同期読み込みを開始したところです。これにより、onload イベントのトリガーがブロックされるという問題が解決されます。
DOMContentLoaded および OnLoad イベントを追加しました。 DOMContentLoaded: ページ (ドキュメント) が解析され、ページ内の dom 要素が使用可能になりました。ただし、ページ内で参照されている画像とサブフレームはまだ読み込まれていない可能性があります。 OnLoad: ページのすべてのリソースがロードされます (画像を含む)。ブラウザの読み込みの進行はこの時点で停止します。これら 2 つの時点は、ページ読み込みタイムラインを 3 つの段階に分割します。
上記はこの問題に対するより良い解決策のように見えますが、html5 ではより簡単な方法である async 属性が提供されています。
this is a test <script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js' async='async'></script> <script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script> <script type="text/javascript"> console.log('hello world'); </script>
async は、HTML5 の新しい属性です。async 属性は、スクリプトが利用可能になると、非同期で実行されることを規定します (ダウンロードされるとすぐに実行されます)。
async 属性は外部スクリプトにのみ適用されることに注意してください (src 属性を使用する場合のみ)
defer 属性は async と一緒によく言及されます:
this is a test <script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js' defer='defer'></script> <script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script> <script type="text/javascript"> console.log('hello world'); </script>
実装効果は似ているように見えますが、本当に同じですか? defer 属性の定義を見てみましょう。
以前は、defer は IE ハックのみをサポートしていましたが、HTML5 の登場により、defer は完全にサポートされるようになりました。 defer 属性は、ページの読み込みが完了するまでスクリプトが実行されないことを指定します。 defer 属性は外部スクリプトにのみ適用されます (src 属性を使用する場合のみ)。 ps: IE でサポートされている遅延は当てはまらないようです。IE には興味がないので詳しくは説明しません。興味があれば、関連情報を確認してください。
asyncとdeferはよく一緒に出てくるので分析してみましょう!
async 属性と defer 属性がない場合 (true に割り当てられている、以下同様)、ブラウザは現在の JS スクリプトをすぐに実行し、async 属性がある場合は後続のドキュメント要素をロードしてレンダリングするプロセスがブロックされます。現在の js と同じであること script.js の読み込みと実行は並行して実行されます (非同期)。 defer 属性がある場合、後続のドキュメント要素の読み込み処理は script.js の読み込みと並行して実行されます。ただし、script.js の実行は、すべての要素 (DOM) が解析された後に完了する必要があります。その後、DOMContentLoaded イベントが発生する前に完了します。
来看一张网上盗的图:
蓝色线代表网络读取,红色线代表执行时间,这俩都是针对脚本的;绿色线代表 HTML 解析。
此图告诉我们以下几个要点(摘自defer和async的区别):
defer 和 async 在网络读取(下载)这块儿是一样的,都是异步的(相较于 HTML 解析)
它俩的差别在于脚本下载完之后何时执行,显然 defer 是最接近我们对于应用脚本加载和执行的要求的
关于 defer,此图未尽之处在于它是按照加载顺序执行脚本的,这一点要善加利用
async 则是一个乱序执行的主,反正对它来说脚本的加载和执行是紧紧挨着的,所以不管你声明的顺序如何,只要它加载完了就会立刻执行
仔细想想,async 对于应用脚本的用处不大,因为它完全不考虑依赖(哪怕是最低级的顺序执行),不过它对于那些可以不依赖任何脚本或不被任何脚本依赖的脚本来说却是非常合适的,最典型的例子:Google Analytics
但是在我看来(以下个人理解,如有出入还望指出),defer在异步加载上的应用并不会比async广。async的英文解释是异步,该属性作用在脚本上,使得脚本加载(下载)完后随即开始执行,和动态插入script标签作用类似(async只支持h5,后者能兼容浏览器);而defer的英文解释是延迟,作用也和字面解释类似,延迟脚本的执行,使得dom元素加载完后才开始有序执行脚本,因为有序,所以会带来另一个问题:
this is a test <script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js' defer='defer'></script> <script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js' defer='defer'></script> <script type="text/javascript" src='index.js' defer='defer'></script> console.log('hello world');
如果执行这段代码,控制台的“hello world”也会迟迟得不到结果。所以我觉得还是async好用,如果要考虑依赖的话,可以选择requirejs、seajs等模块加载器。
JavaScript的异步加载还有一些方式,比如:AJAX eval(使用AJAX得到脚本内容,然后通过eval(xmlhttp.responseText)来运行脚本)、iframe方式等。
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
以上がJavaScriptの非同期読み込みについて詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。