web-dev-qa-db-fra.com

Les scripts différés sont-ils exécutés avant l'événement DOMContentLoaded?

En reportant attirbute dit MDN

Cet attribut booléen est défini pour indiquer à un navigateur que le script doit être exécuté après l'analyse du document, mais avant le déclenchement de DOMContentLoaded. L'attribut deffer ne doit être utilisé que sur des scripts externes. 

Sur DOMContentLoadedMDN dit aussi

L'événement DOMContentLoaded est déclenché lorsque le document HTML initial a été complètement chargé et analysé, sans en attente de feuilles de style ... 

Donc, DOMContentLoaded est déclenché avant que CSSOM ne soit prêt. Cela signifie que les scripts différés sont exécutés avant que CSSOM soit prêt. Mais si cela est vrai, les scripts ne doivent pas pouvoir obtenir les valeurs correctes de la propriété css ni appliquer css correctement. Mais ce n'est pas vrai, nous savons que tous les scripts différés fonctionnent bien. 

  1. La documentation MDN est-elle techniquement incorrecte?
  2. Où puis-je trouver la documentation officielle de DOMContentLoaded`? J'ai cherché dans https://dom.spec.whatwg.org/ mais je ne l'ai pas trouvé. 

P.S: Veuillez noter que Google dit que CSSOM est construit avant d'exécuter un script javascript intégré 

enter image description here

Mais Google est techniquement incorrect. Le JavaScript en ligne est exécuté avant que CSSOM soit prêt. Et de mes tests, j'ai trouvé que MDN est correct et si les fichiers js (différés et non différés) sont téléchargés avant les fichiers css (ou js est en ligne), js est exécuté avant que CSSOM soit prêt. Donc, js pourrait gérer les styles de manière incorrecte. Pour éviter cela, nous avons besoin d'une force de refusion avant toute logique JS. 

Donc, si un utilisateur visite notre site Web avec tous les js requis déjà mis en cache et les css non mis en cache OR, js est téléchargé avant les css, il risque alors de voir la page rendue de manière incorrecte. Pour éviter cela, nous devrions ajouter une force de refusion dans les fichiers js de tous nos sites Web.

13
user31782

J'utilise le chargement de script différé. Il y avait une longue explication technique de la part d'un type qui est un gourou de la performance de site Web bien connu. Il indique clairement que le différé est la voie à suivre (pour ceci et pour cette raison technique, étayée par toutes sortes de données et de graphiques, que beaucoup de gens semblaient sentir comme étant largement ouverte au débat, re: asynchrone). 

Alors j'ai commencé à travailler avec ça. Les scripts différés ont l’avantage de télécharger async, mais de les exécuter dans l’ordre présenté, ce qui peut poser un problème avec async (vous pouvez par exemple charger votre ensemble d’applications avant votre ensemble car vous ne contrôlez pas l’ordre d’exécution des scripts async "dans cet ordre"). 

Cependant, j'ai tout de suite découvert que même si cela résout ce problème, cela pourrait signifier que, selon la manière dont vous récupérez vos bundles, le bundle CSS n'est pas chargé. Ainsi, vous pouvez vous retrouver avec un contenu non stylé, selon la manière dont vous configurez les choses. Notez que pour différer, ils disent aussi que vous ne devriez pas écrire dans le dom, etc. dans ces scripts (ce qui a encore un sens en termes de documentation). 

Il semblerait donc que votre documentation est correcte. L'effet est facilement reproduit.

Comment puis-je m'en sortir? la manière la plus élémentaire est la suivante:

<script src="css.bundle.js"></script>
<script src="vendor.bundle.js" defer></script>
<script src="angular.bundle.js" defer></script>
<script src="app.bundle.js" defer></script>

Cela s'assure que le css se charge en premier, ainsi votre page d'accueil et ainsi de suite s'affichera bien, et s'assurera également que (bien que les trois chargent async), que app.bundle s'exécutera en dernier, s'assurant que toutes les autres dépendances sont en ordre. . 

Donc, vous prenez le minimum absolu de CSS nécessaire pour modifier l'application, la créer en tant qu'ensemble et la charger avant toute autre chose. Sinon, vous pouvez regrouper vos CSS par module/composant, etc. 

Il y a beaucoup plus sur ce sujet et je pourrais probablement en faire plus, mais encore une fois (je vais essayer de trouver la référence), cela a été ouvertement recommandé par cet assistant de performance, alors je l'ai essayé et cela me semble assez efficace.

Edit: Fascinant, en cherchant cette référence (que je n’ai pas encore trouvée), j’ai passé en revue une poignée d’experts sur le sujet. Les recommandations diffèrent énormément. Certains disent que l'async est de loin supérieur à tous égards, certains disent différer. Le jury semble vraiment sur le sujet, dans l'ensemble, je dirais qu'il dépend probablement davantage de la façon dont vous construisez vos scripts que de la question de savoir si l'un est réellement meilleur que l'autre. 

Modifier à nouveau: Voici quelques preuves supplémentaires. J'ai exécuté un analyseur de performances sur un site Web stub en utilisant la séquence de chargement simple ci-dessus, rendant délibérément les scripts naïfs pour qu'ils soient visibles dans une timeline. 

Voici un SS du résultat: il y a quatre cases jaunes ici. Les trois premiers sont les évaluations des scripts. Le quatrième (lorsque vous la survolez dans l'outil, il ne s'agit que de la mémorisation SS) est l'événement DOMContentLoaded (celui avec le coin rouge). 

 Scripts loading/evaluating before DOMContentLoaded event

4
Tim Consolazio

DOMContentLoaded peut être déclenché avant CSSOM, source

L'événement domContentLoaded se déclenche peu de temps après l'analyse du code HTML. le navigateur sait ne pas bloquer en JavaScript et comme il n'y a pas d'autres scripts de blocage de l'analyseur, la construction CSSOM peut également se dérouler en parallèle.

 enter image description here

L'article sur Google Developer décrit async au lieu de defer, mais dans le cas de votre question, cela ne change rien car basé sur article de Steve Sourders sur perfplanet

Les scripts DEFER s'exécutent après DOM Interactive.

et son commentaire sous son article

[...] la spécification indique que les scripts DEFER s'exécutent après domInteractive mais avant domContentLoaded.

Vous pouvez faire votre propre expérience, regardez ci-dessous le code en utilisant defer et la timeline

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Twitter-bootstrap/4.0.0-alpha.6/css/bootstrap.css">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/angular-material/1.1.3/angular-material.css">
</head>
<body>
  <h1>App</h1>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Twitter-bootstrap/4.0.0-alpha.6/js/bootstrap.js" defer></script>
</body>
</html>

 enter image description here

4
hinok