Les fonctionnalités de report et async sont considérées comme deux fonctionnalités que de nombreux développeurs JavaScript sont "familières mais pas familières". D'un point de vue littéral, les fonctions des deux sont faciles à comprendre. et "script asynchrone" respectivement. Cependant, en prenant le report comme exemple, les développeurs ne sont pas nécessairement familiers avec certains détails, tels que : quand un script avec la fonctionnalité defer sera-t-il retardé pour être exécuté ; si les scripts internes et les scripts externes peuvent prendre en charge les scripts defer après le report ; en plus de l'exécution différée, quelles sont les particularités, etc. Cet article combine certains articles existants et la description des deux fonctionnalités dans les documents MDN pour mener une étude plus complète et un résumé du report et de l'async, dans l'espoir d'aider les développeurs à mieux maîtriser ces deux fonctionnalités.
1 Introduction
Dans "Analyse du chargement et de l'exécution du script JavaScript dans l'environnement du navigateur : séquence d'exécution du code" nous avons mentionné que l'exécution du code JavaScript bloquera l'analyse et le rendu de la page ainsi que le téléchargement d'autres ressources. . Bien sûr, étant donné que JavaScript est un langage monothread, ce qui signifie que dans des circonstances normales, le code JavaScript d'une page ne peut être exécuté que dans l'ordre de haut en bas. Bien sûr, comme dans " Analyse de JavaScript. Chargement et exécution du script dans l'environnement du navigateur Comme nous l'avons analysé dans « Séquence d'exécution », dans certains cas, par exemple lors de la saisie d'un script via document.write ou de l'introduction d'un script via la technologie de script dynamique, l'ordre d'exécution du code JavaScript ne ne suivent pas nécessairement l'ordre strict de haut en bas. le report et l'async sont aussi ce que nous appelons des « situations anormales ».
Nous disons souvent que l'exécution de JavaScript est bloquante. Dans le développement réel, le blocage qui nous préoccupe généralement le plus et celui qui affecte le plus l'expérience utilisateur devrait être les aspects suivants :
[1] Blocage de l'analyse et du rendu des pages
[2] Le script d'initialisation de la page que nous avons écrit (généralement le script destiné à écouter l'événement DOMContentLoaded). Cette partie du script est le script que nous souhaitons exécuter en premier, car nous écrirons le code le plus pertinent pour l'interaction de l'utilisateur. ici. )
[3] Blocage du téléchargement de ressources externes sur la page (telles que des images)
Si nous avons une opération de script qui prend du temps et que ce script bloque les trois endroits que nous avons mentionnés ci-dessus, alors les performances ou l'expérience utilisateur de cette page Web seront très mauvaises.
L'intention initiale des deux fonctionnalités defer et async est également de résoudre ou d'atténuer l'impact du blocage sur l'expérience de la page. Analysons ces deux fonctionnalités ci-dessous. Nous comprenons ces deux principalement sous les aspects suivants :
Fonction de 2 reports
2.1 À propos du timing d'exécution du script de report
.
2.2 différer la prise en charge du navigateur
Jetons un coup d'œil à la prise en charge par les navigateurs de la fonctionnalité de report :
Il y a un bug dans les navigateurs IE9 et inférieurs, qui sera expliqué en détail plus tard dans la DÉMO.
2.3 DÉMO : Vérification du fonctionnement de la fonction de report
Nous imitons la méthode utilisée par Olivier Rochard dans "le script defer attribut" pour vérifier la fonction de l'attribut defer :
Nous avons d'abord préparé 6 scripts externes :
1.js:
test = "Je suis responsable du script externe n"
2.js
test = "Je suis un corps externe script n"
3.js
test = "Je suis le script externe du bas n"
defer1.js
test = "Je suis responsable du script de delay externe n"
;
defer2.js
test = "Je suis un script de retard externe du corps n"
;
defer3.js
test = "Je suis le script de retard externe inférieur n"
;
Le code en HTML est :
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"/> <title>defer attribute test</title> <script src="http://lib.sinaapp.com/js/jquery/1.9.1/jquery-1.9.1.min.js"></script> <script type="text/javascript">var test = "";</script> <script src="defer1.js" type="text/javascript" defer="defer"></script> <script src="1.js" type="text/javascript"></script> <script defer="defer"> test += "我是head延迟内部脚本\n"; </script> <script> test += "我是head内部脚本\n"; </script> </head> <body> <button id="test">点击一下</button> <script src="defer2.js" type="text/javascript" defer="defer"></script> <script src="2.js" type="text/javascript"></script> </body> <script src="defer3.js" type="text/javascript" defer="defer"></script> <script src="3.js" type="text/javascript"></script> <script> $(function(){ test += "我是DOMContentLoaded里面的脚本 "; }) window.onload = function(){ test += "我是window.onload里面的脚本 "; var button = document.getElementById("test"); button.onclick = function(){ alert(test); } } </script> </html>
Dans le code, afin de faciliter l'implémentation de l'événement DOMContentLoaded, nous avons introduit jQuery (des articles ultérieurs expliqueront comment implémenter vous-même un DOMContentLoaded compatible. Ensuite, nous avons introduit des scripts de retard dans la tête, à l'intérieur du corps et à l'extérieur du). corps du script et des scripts normaux, et enregistre l'état d'exécution de chaque morceau de code via une chaîne globale. Jetons un coup d'œil aux résultats d'exécution dans chaque navigateur :
.
IE7 | IE9 | IE10 | CHROME | firefox | ||||||||||
|
<🎜>Je suis responsable script externe<🎜> Je suis responsable du script interne<🎜> Je suis un script externe au corps<🎜> Je suis le script externe du bas <🎜> Je suis responsable du script de delay externe <🎜> Je suis responsable du script interne du retard<🎜> Je suis un script de retard externe au corps <🎜> Je suis le script de delay externe du bas <🎜> Je suis le script dans DOMContentLoaded<🎜> Je suis le script dans window.onload<🎜> | <🎜>Je suis responsable script externe<🎜> Je suis en tête du script interne retardé<🎜> Je suis responsable du script interne<🎜> Je suis un script externe au corps<🎜> Je suis le script externe du bas <🎜> Je suis responsable du script de delay externe <🎜> Je suis un script de retard externe au corps <🎜> Je suis le script de delay externe du bas <🎜> Je suis le script dans DOMContentLoaded<🎜> Je suis le script dans window.onload<🎜> | <🎜>Je suis responsable script externe<🎜> Je suis en tête du script interne retardé<🎜> Je suis responsable du script interne<🎜> Je suis un script externe au corps<🎜> Je suis le script externe du bas <🎜> Je suis responsable du script de delay externe <🎜> Je suis un script de retard externe au corps <🎜> Je suis le script de delay externe du bas <🎜> Je suis le script dans DOMContentLoaded<🎜> Je suis le script dans window.onload<🎜> | <🎜> |
从输出的结果中我们可以确定,只有IE9及以下浏览器支持内部延迟脚本,并且defer后的脚本都会在DOMContentLoaded事件之前触发,因此也是会堵塞DOMContentLoaded事件的。
2.4 DEMO:IE<=9的defer特性bug
从2.3节中的demo可以看出,defer后的脚本还是能够保持执行顺序的,也就是按照添加的顺序依次执行。而在IE<=9中,这个问题存在一个bug:假如我们向文档中增加了多个defer的脚本,而且之前的脚本中有appendChild,innerHTML,insertBefore,replaceChild等修改了DOM的接口调用,那么后面的脚本可能会先于该脚本执行。可以参考github的issue:https://github.com/h5bp/lazyweb-requests/issues/42
我们通过DEMO验证一下,首先修改1.js的代码为(这段代码只为模拟,事实上这段代码存在极大的性能问题):
document.body.innerHTML = "
2.js
alert("我是第2个脚本");
修改HMTL中的代码为:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"/> <title>defer bug in IE=9 test</title> <script src="1.js" type="text/javascript" defer="defer"></script> <script src="2.js" type="text/javascript" defer="defer"></script> </head> <body> </body> </html>
正常情况下,浏览器中弹出框的顺序肯定是:我是第1个脚本-》我是第2个脚本,然而在IE<=9中,执行结果却为:我是第2个脚本-》我是第1个脚本,验证了这个bug。
2.5 defer总结
在总结之前,首先要说一个注意点:正如标准中提到的,defer的脚本中不应该出现document.write的操作,浏览器会直接忽略这些操作。
总的来看,defer的作用一定程度上与将脚本放置在页面底部有一定的相似,但由于IE<=9中的bug,如果页面中出现多个defer时,脚本的执行顺序可能会被打乱从而导致代码依赖可能会出错,因此实际项目中很少会使用defer特性,而将脚本代码放置在页面底部可以替代defer所提供的功能。
3 async特性
3.1 关于async脚本的执行时机
async特性是HTML5中引入的特性,使用方式为:async="async",我们首先看一下标准中对于async特性的相关描述:
async:If the async attribute is present, then the script will be executed asynchronously, as soon as it is available.
需要指出,这里的异步,指的其实是异步加载而不是异步执行,也就是说,浏览器遇到一个async的script标签时,会异步的去加载(个人认为这个过程主要是下载的过程),一旦加载完毕就会执行代码,而执行的过程肯定还是同步的,也就是阻塞的。我们可以通过下图来综合理解defer和async:
这样来看的话,async脚本的执行时机是无法确定的,因为脚本何时加载完毕也是不确定的。我们通过下面的demo来感受一下:
async1.js
alert("我是异步的脚本");
HTML代码:
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title>async attribute test</title> <script src="/delayfile.php?url=http://localhost/js/load/async1.js&delay=2" async="async" type="text/javascript"></script> <script> alert("我是同步的脚本"); </script> </head> <body> </body> </html>
여기서는 "브라우저 환경의 JavaScript 스크립트 로딩 및 실행 분석: 코드 실행 순서"에서 지연 파일 스크립트를 빌려 비동기를 지원하는 브라우저에서 이 스크립트의 팝업 상자 순서가 일반적입니다. : 나는 동기 스크립트입니다 -> 나는 비동기 스크립트입니다.
3.2 브라우저 비동기 지원
비동기 기능에 대한 브라우저 지원을 살펴보겠습니다.
보시다시피 IE10만 비동기 기능을 지원하고, Opera Mini는 비동기 기능을 지원하지 않으며, 비동기는 내부 스크립트를 지원하지 않습니다.
3.3 비동기 요약
async는 비동기식 스크립트를 의미합니다. 즉, 스크립트가 비동기적으로 로드되는 과정은 블로킹을 일으키지 않지만, 비동기 스크립트의 실행 시기도 불확실하고, 실행 순서도 불확실하므로 비동기를 사용하는 스크립트는 반드시 코드(예: 타사 통계 코드 또는 광고 코드)에 의존하지 않는 스크립트. 그렇지 않으면 실행 오류가 발생합니다.
지연과 비동기 사이의 4가지 우선순위 문제
이 표준은 다음과 같이 규정하고 있습니다.
[1]