webpack ≥ 2, Promise et polyfill

Si vous utilisez webpack ≥ 2 vous savez certainement que ce dernier fait usage de Promise pour les chargements asynchrones de module. Son support a beau être plutôt satisfaisant, si vous lisez ceci il ne l’est certainement pas assez pour vous, d’où la nécessité d’un polyfill. De quelle façon doit-on alors charger ce dernier ? Voyons ce qu’en dit Philip Walton :

  • Le code du polyfill ne doit pas être téléchargé par les navigateurs n’en ayant pas besoin.
  • Aucune requête supplémentaire ne doit être envoyée par les navigateurs ne nécessitant pas le polyfill.
  • Aucune technique néfaste au rendu, parsing ou au preload scanner ne doit être utilisée.
  • Le polyfill doit être fonctionnel au premier chargement comme aux suivants.
  • Le code de chargement du polyfill doit être léger et simple à maintenir.
  • Le code du polyfill doit être le plus léger possible.

Généralement il suffit de tester le support de la fonctionnalité et de charger dynamiquement son polyfill si elle n’est pas implémentée. Le problème ici, c’est que webpack fournit le code de chargement dynamique, et que celui-ci repose sur Promise ; nous ne pouvons donc pas utiliser webpack pour charger le polyfill. Nous allons donc le faire nous-mêmes.

Tester le support

Nous avons de la chance, tester le support de Promise est trivial.

typeof Promise === "undefined"  

Nous devrons charger le polyfill si l’expression ci-dessus retourne true.

Charger le polyfill

J’ai choisi celui-ci pour l’exemple mais vous être libre de choisir votre préféré.

var polyfill = document.createElement("script");  
polyfill.src = "https://cdnjs.cloudflare.com/ajax/libs/es6-promise/4.1.0/es6-promise.auto.min.js";  
polyfill.async = false;  
document.head.appendChild(polyfill);  

Nous avons ici du dynamic script loading classique à une exception : la propriété async du script à false permet de maintenir l’ordre d’exécution, ce qui est requis pour être sûr que le polyfill a été appliqué au moment où il doit servir.

Placer le script

Pour respecter les points ci-dessus notre script ne doit pas se trouver dans un fichier car cela obligerait tous les navigateurs à le charger. Il se trouvera donc entre balises <script>. Pour éviter d’attendre la construction du CSSOM avant son exécution on le placera avant tout CSS. Bien sûr, il faudra également le placer avant tout script relatif à webpack !

<script>  
if (typeof Promise === "undefined") {  
    var polyfill = document.createElement("script");
    polyfill.src = "https://cdnjs.cloudflare.com/ajax/libs/es6-promise/4.1.0/es6-promise.auto.min.js";
    polyfill.async = false;
    document.head.appendChild(polyfill);
}
</script>  
<link rel="stylesheet" href="style.css">  
<script src="script.js"></script>  

Et voilà, webpack ≥ 2 peut maintenant charger des modules de manière asynchrone même sans support natif de Promise !