Utilisation de modules JavaScript en production: situation actuelle. 2e partie

Aujourd'hui, nous publions la deuxième partie de la traduction du matériel, qui est consacrée à l'utilisation des modules JS dans la production.



→ Au fait, voici la première partie de l' article.

Importation dynamique


L'un des inconvénients de l'utilisation d'expressions d'importation réelles pour séparer le code et charger les modules est que la tâche de travailler avec des navigateurs qui ne prennent pas en charge les modules incombe au développeur.

Et si vous souhaitez utiliser des commandes dynamiques import() pour organiser le chargement de code paresseux, vous devrez, entre autres, faire face au fait que certains navigateurs, bien que, très certainement, les modules de support , ne prennent toujours pas en charge les commandes dynamiques import () (Edge 16–18, Firefox 60–66, Safari 11, Chrome 61–63).

Heureusement, ce problème nous aidera à résoudre un polyfill petit (environ 400 octets) et extrêmement rapide.

L'ajout de ce polyfill à un projet Web est très simple. Il vous suffit de l'importer et de l'initialiser au point d'entrée principal de l'application (avant d'appeler l'une des commandes import() du code):

 import dynamicImportPolyfill from 'dynamic-import-polyfill'; //         .   //    ,       . dynamicImportPolyfill.initialize({modulePath: '/modules/'}); 

Et la dernière chose à faire pour que ce schéma fonctionne, est de dire à Rollup qu'il doit renommer les commandes dynamic import() qui apparaissent dans le code en utilisant le nom que vous choisissez (via l'option output.dynamicImportFunction ). Un polyfill qui implémente la fonction d'importation dynamique utilise le nom __import__ par défaut, mais il peut être personnalisé .

La raison pour laquelle vous devez renommer import() expressions import() est que l' import est, en JavaScript, un mot-clé. Cela signifie qu'il est impossible, par polyfill, d'organiser le remplacement de la commande standard import() par une commande du même nom. Si vous essayez de faire cela, une erreur de syntaxe se produira.

Mais c'est très bien que Rollup renomme les commandes lors de la construction du projet, car cela signifie que vous pouvez utiliser des commandes standard dans le code source. De plus, à l'avenir, lorsque le polyfill ne sera plus nécessaire, le code source du projet n'aura pas à être réécrit, changeant pour import ce qui était précédemment nommé d'une manière ou d'une autre.

Chargement efficace de JavaScript


Chaque fois que vous utilisez la séparation de code, cela ne fait pas de mal d'organiser le préchargement de tous les modules dont vous savez qu'ils seront chargés très prochainement (par exemple, ce sont tous les modules dans l'arborescence des dépendances du module principal, qui est le point d'entrée du projet).

Mais lorsque nous chargeons de vrais modules JavaScript (via <script type="module"> puis via les commandes d' import correspondantes), nous devons utiliser l'attribut modulepreload au lieu du préchargement habituel, qui est destiné uniquement aux scripts classiques.

 <link rel="modulepreload" href="/modules/main.XXXX.mjs"> <link rel="modulepreload" href="/modules/npm.pkg-one.XXXX.mjs"> <link rel="modulepreload" href="/modules/npm.pkg-two.XXXX.mjs"> <link rel="modulepreload" href="/modules/npm.pkg-three.XXXX.mjs"> <!-- ... --> <script type="module" src="/modules/main.XXXX.mjs"></script> 

En fait, la modulepreload est nettement meilleure que le mécanisme de preload traditionnel dans la précharge de vrais modules. Le fait est que lorsque vous l'utilisez, ce n'est pas seulement que le fichier est téléchargé. De plus, il démarre immédiatement, en dehors du thread principal, l'analyse et la compilation du script. Un preload régulier ne peut pas faire cela car il ne sait pas, lors du préchargement, si le fichier sera utilisé comme module ou comme script normal.

Cela signifie que le chargement des modules à l'aide de l'attribut modulepreload souvent plus rapide et que lorsque les modules sont initialisés, il est moins susceptible de créer une charge excessive sur le thread principal, ce qui provoque des problèmes d'interface.

Création d'une liste de modules pour le préchargement


Le fragment d'entrée dans l'objet bundle Rollup contient une liste complète des importations dans leurs arborescences de dépendances statiques. Par conséquent, dans le hook Rollup generateBundle , il est facile d'obtenir une liste de fichiers qui doivent être préchargés.

Bien que des plugins puissent être trouvés dans npm pour générer des listes de préchargement de module, la création d'une liste similaire pour chaque point d'entrée dans l'arborescence des dépendances ne nécessite que quelques lignes de code. Par conséquent, je préfère créer ces listes manuellement, en utilisant quelque chose comme ce code:

 {  generateBundle(options, bundle) {    //         .    const modulepreloadMap = {};    for (const [fileName, chunkInfo] of Object.entries(bundle)) {      if (chunkInfo.isEntry || chunkInfo.isDynamicEntry) {        modulepreloadMap[chunkInfo.name] = [fileName, ...chunkInfo.imports];      }    }    //  -   ...    console.log(modulepreloadMap);  } } 

Voici, par exemple, comment j'ai créé une liste de chargement de module pour philipwalton.com et pour mon application de démonstration , dont nous parlerons ci-dessous.

Veuillez noter que bien que l'attribut modulepreload nettement meilleur que le preload classique pour le chargement des scripts de module, il a la pire prise en charge du navigateur (actuellement, seul Chrome le prend en charge). Si une partie importante de votre trafic ne provient pas de Chrome, dans votre cas, il peut être judicieux d'utiliser la preload régulière au lieu de la preload du preload .

Concernant l'utilisation de la preload , je voudrais vous avertir de quelque chose. Le fait est que lors du chargement de scripts à l'aide du preload , contrairement au modulepreload , ces scripts n'entrent pas dans la carte du module du navigateur. Cela signifie qu'il est possible que les demandes de préchargement puissent être exécutées plusieurs fois (par exemple, si le module importe le fichier avant que le navigateur ait fini de le précharger).

Pourquoi déployer de vrais modules en production?


Si vous utilisez déjà un bundler comme webpack , ainsi que si vous utilisez déjà le fractionnement de code et le préchargement des fichiers correspondants (similaire à ce que je viens de dire), alors vous vous demandez peut-être si vous devez passer à une stratégie concentré sur l'utilisation de vrais modules. Il y a plusieurs raisons qui me font croire que vous devriez envisager de passer aux modules. De plus, je pense qu'il est préférable de convertir un projet en modules réels que d'utiliser des scripts classiques avec leur propre code conçu pour charger des modules.

▍ Réduire la quantité totale de code


Si le projet utilise de vrais modules, les utilisateurs de navigateurs modernes n'auront pas à télécharger de code supplémentaire conçu pour charger des modules ou pour gérer les dépendances. Par exemple, lorsque vous utilisez de vrais modules, vous n'aurez pas besoin de charger les mécanismes d'exécution et le manifeste du webpack .

▍ Code de précharge amélioré


Comme mentionné dans la section précédente, l'utilisation de l'attribut modulepreload permet de charger du code, de l' modulepreload et de le compiler en dehors du thread principal. Tout le reste, par rapport à l'attribut de preload , reste le même. Cela signifie que grâce au modulepreload pages deviennent interactives plus rapidement, et cela réduit la probabilité de bloquer le flux principal pendant l'interaction avec l'utilisateur.

Par conséquent, quelle que soit la taille du code d'application divisé en fragments, il sera beaucoup plus productif de télécharger ces fragments à l'aide des commandes d'importation et de l'attribut modulepreload que de les charger à l'aide de la balise de script habituelle et de l'attribut de preload habituel (surtout si les balises correspondantes sont générées dynamiquement et ajouté au DOM lors de l'exécution).

En d'autres termes, un bundle de cumul de code de projet, composé de 20 fragments de module, se chargera plus rapidement qu'un bundle du même projet, composé de 20 fragments de script classiques préparés par webpack (non pas à cause de l'utilisation de webpack, mais parce que que ce ne sont pas de vrais modules).

▍ Améliorer l'orientation future du code


De nombreuses nouvelles fonctionnalités des navigateurs sont basées sur des modules et non sur des scripts classiques. Cela signifie que si vous prévoyez d'utiliser ces fonctionnalités, votre code doit être présenté sous la forme de vrais modules. Cela ne devrait pas être quelque chose de transpilé dans ES5 et chargé avec les moyens de la balise de script classique (c'est le problème que j'ai écrit lorsque j'ai essayé d'utiliser l' API expérimentale de stockage KV ).

Voici quelques-unes des nouvelles fonctionnalités de navigateur les plus intéressantes qui se concentrent exclusivement sur les modules:


Prise en charge du navigateur hérité


À l'échelle mondiale, plus de 83% des navigateurs prennent en charge les modules JavaScript (y compris l'importation dynamique), par conséquent, la plupart des utilisateurs pourront travailler avec un projet qui est passé à des modules sans aucun effort particulier de la part des développeurs de ce projet.

Dans le cas des navigateurs qui prennent en charge les modules mais ne prennent pas en charge l'importation dynamique, il est recommandé d'utiliser le polyfill dynamic-import-polyfill décrit ci-dessus. Puisqu'il est très petit et, si possible, utilise la méthode standard import() navigateur import() , l'utilisation de ce polyfill n'a presque aucun effet sur la taille ou les performances du projet.

Si nous parlons de navigateurs qui ne supportent absolument pas les modules, alors, pour organiser le travail avec eux, vous pouvez utiliser le modèle module / nomodule .

â–ŤExemple de fonctionnement


Puisqu'il est toujours plus facile de parler de compatibilité entre navigateurs que de la réaliser, j'ai créé une application de démonstration qui utilise les technologies décrites ci-dessus.

Cette application fonctionne dans les navigateurs comme Edge 18 et Firefox ESR, qui ne prennent pas en charge import() commandes d' import() dynamique import() . De plus, il fonctionne dans des navigateurs comme Internet Explorer 11, qui ne prennent pas en charge les modules.

Afin de montrer que la stratégie discutée ici convient non seulement aux projets simples, j'ai utilisé de nombreuses fonctionnalités dans cette application qui sont nécessaires aujourd'hui dans les grands projets:

  • Transformation de code Ă  l'aide de Babel (y compris JSX).
  • DĂ©pendances CommonJS (par exemple react et react-dom).
  • DĂ©pendances CSS.
  • Hachage de ressources
  • SĂ©paration de code
  • Importation dynamique (avec un repli comme polyfill).
  • ImplĂ©mentation du modèle module / nomodule.

Le code du projet peut être trouvé sur GitHub (c'est-à-dire que vous pouvez bifurquer le référentiel et construire le projet vous-même), la version de démonstration est hébergée sur Glitch , ce qui vous permet de l'expérimenter.

La chose la plus importante dans le projet de démonstration est la configuration Rollup , car elle détermine la façon dont les modules résultants sont créés.

Résumé


J'espère que ce matériel vous a convaincu non seulement de la possibilité de déployer des modules JavaScript standard en production, mais aussi qu'il peut vraiment améliorer le temps de chargement du site et ses performances.

Voici un bref aperçu des étapes à suivre pour implémenter les modules dans le projet:

  • Utilisez un bundler, parmi les formats de sortie pris en charge par lesquels il existe des modules ES2015.
  • Approchez de manière agressive la sĂ©paration du code (si possible, jusqu'Ă  l'allocation des dĂ©pendances de node_modules en fragments sĂ©parĂ©s).
  • PrĂ©chargez tous les modules qui se trouvent dans votre arborescence de dĂ©pendances statiques (en utilisant modulepreload ).
  • Utilisez polyfill pour les navigateurs qui ne prennent pas en charge import() instructions import() dynamiques.
  • Utilisez le modèle de module / nomodule pour organiser le travail avec les navigateurs qui ne prennent pas en charge les modules.

Si vous utilisez déjà Rollup pour construire le projet, je voudrais que vous essayiez de ce dont je parlais ici et que vous alliez déployer de vrais modules en production (en utilisant des techniques de séparation de code et d'importation dynamique). Si vous le faites, faites-moi savoir comment vous allez. Je suis intéressé à connaître les problèmes et les cas réussis d'introduction de modules.

Il est très clair que les modules sont l’avenir de JavaScript. J'aimerais voir, et de préférence bientôt, comment les outils que nous utilisons et les bibliothèques auxiliaires adoptent cette technologie. J'espère que ce matériel pourra au moins aider un peu ce processus.

Chers lecteurs! Utilisez-vous des modules JS en production?

Source: https://habr.com/ru/post/fr466539/


All Articles