JavaScript の非同期読み込みに関連するいくつかの問題

小云云
リリース: 2018-02-22 09:06:54
オリジナル
1665 人が閲覧しました

デフォルトの js は同期的に読み込まれます。ここでの「読み込み」は、「ダウンロード」ではなく、解析と実行として理解できます。ブラウザの最新バージョンでは、コードによって要求されたリソースがウォーターフォール形式で読み込まれます。ブロックではありませんが、js の実行は常にブロックされます。これはどのような問題を引き起こしますか?インデックス ページで js をロードする必要があるが、リクエストの 1 つが長時間応答を取得しない場合、後続の js コードの実行 (同期ロード) がブロックされ、ページのレンダリングを続行できなくなります (js 導入が失敗した場合)はラベルの後の head にあります)。


<script type="text/javascript" src=&#39;http://china-addthis.googlecode.com/svn/trunk/addthis.js&#39;></script>
<script type="text/javascript" src=&#39;http://libs.baidu.com/jquery/2.0.0/jquery.min.js&#39;></script>
this is a test
ログイン後にコピー

たとえば、上記のコードは、index.html ファイルとして保存されますが、コードが実行された後、ページは空白のままになります。リクエストされた js が長時間ロードできないため (Google がブロックされているなどの理由で)、後続のコードの実行がブロックされ、ページをレンダリングできません。おそらく、js コードを の前に置くと、ページが最初にレンダリングされると提案するかもしれません。良い方法です。js を後ろに配置してみます:


this is a test
<script type="text/javascript" src=&#39;http://china-addthis.googlecode.com/svn/trunk/addthis.js&#39;></script>
<script type="text/javascript" src=&#39;http://libs.baidu.com/jquery/2.0.0/jquery.min.js&#39;></script>
ログイン後にコピー

ページは即座にレンダリングされ、「これはテストです」もすぐに前景に表示され、世界は穏やかに見えますが、:


this is a test
<script type="text/javascript" src=&#39;http://china-addthis.googlecode.com/svn/trunk/addthis.js&#39;></script>
<script type="text/javascript" src=&#39;http://libs.baidu.com/jquery/2.0.0/jquery.min.js&#39;></script>
ログイン後にコピー

が前面にあります。コードに基づいてコードが追加されただけですが、「hello world」が長時間コンソールに出力されませんでした。明らかに、前の js リクエストが次のコードの読み込みをブロックしました。 js の読み込み位置を変更してもページのレンダリングが変わるだけであることに突然気づきましたが、js の読み込みには役に立たず、js は依然としてブロックされてしまいます。

js の非同期読み込みを実装する

私たちの要件は非常に単純に思えます。より簡単に言うと、Google が提供する最初の js をリクエストしながら、次の js を実行し続けるだけです。 , これはjsの非同期読み込みを実装することです。

最も一般的な方法は、スクリプト タグを動的に生成することです:


<body>
 this is a test
 <script type="text/javascript">
  ~function() {
   var s = document.createElement(&#39;script&#39;);
   s.src = &#39;http://china-addthis.googlecode.com/svn/trunk/addthis.js&#39;;
   document.body.appendChild(s);
  }();
 </script>
 <script type="text/javascript" src=&#39;http://libs.baidu.com/jquery/2.0.0/jquery.min.js&#39;></script>
 <script type="text/javascript">
  console.log(&#39;hello world&#39;);
 </script>
</body>
ログイン後にコピー

しかし、この読み込み方法では、多くのページのコードが読み込みが完了する前に onload イベントがトリガーされないという問題がまだあります。追加のレンダリング作業などを実行する必要があるため、一部のページの初期化処理がブロックされます:


<body>
 this is a test
 <script type="text/javascript">
  ~function() {
   // function async_load() {
    var s = document.createElement(&#39;script&#39;);
    s.src = &#39;http://china-addthis.googlecode.com/svn/trunk/addthis.js&#39;;
    document.body.appendChild(s);
   // }
   // window.addEventListener(&#39;load&#39;, async_load, false);
  }();

  window.onload = function() {
   var txt = document.createTextNode(&#39; hello world&#39;);
   document.body.appendChild(txt);
  };
 </script>
 <script type="text/javascript" src=&#39;http://libs.baidu.com/jquery/2.0.0/jquery.min.js&#39;></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=&#39;http://china-addthis.googlecode.com/svn/trunk/addthis.js&#39; async=&#39;async&#39;></script>
<script type="text/javascript" src=&#39;http://libs.baidu.com/jquery/2.0.0/jquery.min.js&#39;></script>
<script type="text/javascript">
 console.log(&#39;hello world&#39;);
</script>
ログイン後にコピー

async は HTML5 の新しい属性です。 async 属性は、スクリプトが利用可能になると非同期で実行されることを規定します (ダウンロードされるとすぐに実行されます)。

async 属性は外部スクリプトにのみ適用されることに注意してください (src 属性を使用する場合のみ)

defer 属性は async と一緒によく言及されます:


this is a test
<script type="text/javascript" src=&#39;http://china-addthis.googlecode.com/svn/trunk/addthis.js&#39; defer=&#39;defer&#39;></script>
<script type="text/javascript" src=&#39;http://libs.baidu.com/jquery/2.0.0/jquery.min.js&#39;></script>
<script type="text/javascript">
 console.log(&#39;hello world&#39;);
</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 の違いから抜粋):

  1. defer と async は、ネットワーク読み取り (ダウンロード) という点では同じであり、どちらも (HTML 解析と比較して) 非同期です。

  2. 2 つの違いは、スクリプトがダウンロードされた後にいつ実行されるかにあります。明らかに、defer がアプリケーション スクリプトの読み込みと実行の要件に最も近いです。

  3. 关于 defer,此图未尽之处在于它是按照加载顺序执行脚本的,这一点要善加利用

  4. async 则是一个乱序执行的主,反正对它来说脚本的加载和执行是紧紧挨着的,所以不管你声明的顺序如何,只要它加载完了就会立刻执行

  5. 仔细想想,async 对于应用脚本的用处不大,因为它完全不考虑依赖(哪怕是最低级的顺序执行),不过它对于那些可以不依赖任何脚本或不被任何脚本依赖的脚本来说却是非常合适的,最典型的例子:Google Analytics

但是在我看来(以下个人理解,如有出入还望指出),defer在异步加载上的应用并不会比async广。async的英文解释是异步,该属性作用在脚本上,使得脚本加载(下载)完后随即开始执行,和动态插入script标签作用类似(async只支持h5,后者能兼容浏览器);而defer的英文解释是延迟,作用也和字面解释类似,延迟脚本的执行,使得dom元素加载完后才开始有序执行脚本,因为有序,所以会带来另一个问题:


this is a test
<script type="text/javascript" src=&#39;http://china-addthis.googlecode.com/svn/trunk/addthis.js&#39; defer=&#39;defer&#39;></script>
<script type="text/javascript" src=&#39;http://libs.baidu.com/jquery/2.0.0/jquery.min.js&#39; defer=&#39;defer&#39;></script>
<script type="text/javascript" src=&#39;index.js&#39; defer=&#39;defer&#39;></script>
console.log(&#39;hello world&#39;);
ログイン後にコピー

如果执行这段代码,控制台的“hello world”也会迟迟得不到结果。所以我觉得还是async好用,如果要考虑依赖的话,可以选择requirejs、seajs等模块加载器。

总结

JavaScript的异步加载还有一些方式,比如:AJAX eval(使用AJAX得到脚本内容,然后通过eval(xmlhttp.responseText)来运行脚本)、iframe方式等。

相关推荐:

Angular结合zTree异步加载节点数据实例分享

JavaScript文件的同步和异步加载的实现代码

使用DataTable插件实现异步加载数据详解


以上がJavaScript の非同期読み込みに関連するいくつかの問題の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート