Modules ES6 dans le navigateur: sont-ils prêts ou non?

Avez-vous entendu parler de l'utilisation des modules ES6 dans un navigateur? En fait, ce sont des modules ES6 ordinaires. Ils s'appliquent uniquement au code destiné aux navigateurs.



Si quelqu'un ne sait pas - c'est à quoi cela ressemble.

Il y a une page index.html:

<!DOCTYPE html>
<html>
  <body>
    <div id="app"></div>
  
    <script type="module" src="index.mjs"></script>
    <!--     "module" -->
  </body>
</html>

Il existe un fichier index.mjsreprésentant un module connecté à la page:

import { doSomething } from './utils.mjs';

doSomething('Make me a sandwich');

Ce module, à son tour, importe la fonction doSomethingdu module utils.mjs:

export const doSomething = message => {
  console.log('No.');
};

Les modules ES6 existent depuis de nombreuses années. Et peut-être que vous songez à les essayer. Je les ai utilisés pendant environ un mois dans mon propre projet. En même temps, je suis arrivé à la conclusion que ...

Suspence!

À propos de la prise en charge du navigateur pour les modules


Avant de parler de ce que je comprends, je suggère de jeter un oeil à la prise en charge du navigateur pour les modules. J'écris ce matériel début 2020, donc le niveau de support des modules est assez impressionnant à 90%.


Prise en charge des modules avec navigateurs selon caniuse.com,

je suis assez content de ces 90% (je ne prends pas en compte les besoins de 10% des utilisateurs), même si vous voudrez peut-être être plus responsable. Mais, même en considérant cela, si votre projet n'est pas conçu pour IE, UC ou Opera Mini, ce niveau de support signifie que près de 100% de votre public cible pourra travailler avec votre projet sans problème.

Certes, la prise en charge du navigateur pour les modules n'est qu'un début. Ici, je veux trouver la réponse à trois questions qui se sont posées au tout début de mon parcours vers les modules:

  • L'utilisation de modules dans les navigateurs présente-t-elle des avantages?
  • Et les inconvénients?
  • Je suis sûr que j'avais une troisième question, mais maintenant je ne m'en souviens plus.

Voyons ça ...

Quels sont les avantages d'utiliser des modules dans les navigateurs?


C'est du pur JavaScript! Pas de pipeline de projet d'assemblage, pas de fichier de configuration Webpack de 400 lignes, pas de Babel, pas de plugins et de presets, et pas de modules supplémentaires de 45 npm. Seulement vous et votre code.

Il y a quelque chose de rafraîchissant à écrire du code qui sera exécuté dans le navigateur exactement sous la forme dans laquelle il a été créé. Cela peut nécessiter un peu plus d'efforts, mais le résultat est très agréable. Voici comment conduire une voiture avec une transmission manuelle.

Donc. Les avantages des modules ES6 sont le JavaScript pur, c'est le manque d'assemblage, de configuration du projet et le rejet des dépendances devenues inutiles. Quoi d'autre? Y a-t-il autre chose?

Non, rien de plus.

Quels sont les inconvénients de l'utilisation de modules dans les navigateurs?


▍Comparaison des modules et des bundlers


Lorsque vous pensez à utiliser des modules ES6 dans un navigateur, vous devez réellement choisir entre utiliser des modules et des bundlers comme Webpack, Parcel ou Rollup.

Vous pouvez (probablement) utiliser les deux, mais en réalité, si vous prévoyez d'exécuter le code via le bundler, il n'y a aucune raison d'utiliser la conception <script type="module">pour charger les fichiers du bundle.

Donc, en supposant que quelqu'un envisage la possibilité d'utiliser des modules ES6 au lieu d'un bundler, voici ce qu'il devra abandonner:

  • Minification du code fini.
  • Fonctionnalités JavaScript de pointe.
  • Syntaxe différente de la syntaxe JavaScript (React, TypeScript, etc.).
  • Modules NPM.
  • Mise en cache

Examinez plus en détail les trois premiers paragraphes de cette liste.

▍ Taille du fichier


La taille de mon projet, qui utilise des modules, est de 34 Ko. Comme je n'utilise pas l'étape de construction du projet, le code transmis sur le réseau contient des noms de variables très longs; il contient de nombreux commentaires. De plus, il représente tout un tas de petits fichiers, ce qui n'est pas très bon en termes de compression de données.

Si je mettais tout cela dans un paquet en utilisant Parcel , la taille de ce qui se passerait serait de 18 Ko. Ma calculatrice Casual Casio signale que c'est «environ la moitié» du code de projet dans lequel le bundler n'est pas utilisé. Cela est dû en partie au fait que Parcel minimise les fichiers, mais aussi parce que les matériaux avec cette approche sont mieux compressés à l'aide de gzip.

La compression des données attire notre attention sur un autre problème: la façon dont les fichiers sont organisés (dans le sens de la commodité du développement) est directement transférée à la façon dont les fichiers sont transférés sur le réseau. Et la façon dont les fichiers sont organisés pendant le développement ne correspond pas nécessairement à la façon dont vous souhaitez les voir sur le site. Par exemple, 150 modules (fichiers) peuvent avoir du sens lorsque vous travaillez sur un projet. Et les mêmes matériaux transférés au navigateur peuvent être justifiés pour s'organiser en 12 paquets.

Je vais expliquer cette idée. Je ne dis pas que l'utilisation de modules signifie que les fichiers ne peuvent pas être regroupés et minifiés (le fait que cela ne puisse pas être fait est une illusion). Je veux juste dire que cela n'a aucun sens de faire les deux.

Oui, voici une observation amusante. Dans mon application, jusqu'à ce que j'écrive cette section, des modules étaient utilisés (je souligne!). J'ai installé Parcel pour calculer la taille d'assemblage correcte. Et maintenant, je ne vois aucune raison de revenir aux modules normaux. Eh bien, n'est-ce pas intéressant!

▍Vie sans transpilation


Au fil des ans, j'ai l'habitude d'utiliser les dernières constructions de syntaxe JavaScript et le merveilleux outil Babel, qui les transforme en code que tous les navigateurs comprennent. Je m'y suis tellement habitué que j'ai rarement au moins pensé à la prise en charge du navigateur (bien sûr, à l'exception de DOM et CSS).

Quand j'ai essayé pour la première fois type=«module», j'allais tout faire moi-même. Mon objectif était de nouveaux navigateurs, donc j'ai pensé que je pouvais utiliser du JavaScript moderne, auquel je suis habitué.

Mais la réalité n'était pas si rose. Essayez de répondre aux questions suivantes rapidement et sans chercher nulle part. Est-ce que Edge prend en chargeflatMap()? Safari prend-il en charge l'attribution destructive (objets et tableaux)? Chrome prend-il en charge les virgules après les derniers arguments de la fonction? Firefox prend-il en charge l'exponentiation?

Par exemple, j'ai dû chercher des réponses à ces questions, effectuer des tests inter-navigateurs. Mais j'ai utilisé tout cela pendant des siècles. Dans toute application suffisamment grande, cela entraînerait très probablement des erreurs de production.

Cela signifie également que je ne serai pas en mesure d'utiliser l'innovation JavaScript la plus remarquable depuis l'avènement de la méthode de tableau slice()- l' opérateur de séquence dit facultatif . J'ai utilisé des designs magiques commeprop?.valueenviron un mois (à partir du moment où l'outil Create React App a commencé à les prendre en charge sans aucun paramètre supplémentaire). Mais c'est déjà gênant pour moi de travailler sans eux.

▍ Mise en cache des charges


La mise en cache était pour moi la plus grande pierre d'achoppement. En fait, le fait que la solution au problème de mise en cache se soit avérée assez intéressante s'est avéré être la principale raison pour laquelle j'ai décidé d'écrire ce matériel.

Comme vous en êtes sûr, vous savez, lorsque les matériaux sont traités à l'aide d'un bundler, chacun des fichiers résultants obtient un nom unique - en quelque sorte index.38fd9e.js. Le contenu d'un fichier portant ce nom ne change jamais (jamais). Par conséquent, il peut être mis en cache par le navigateur pour une durée illimitée. Si un tel fichier est téléchargé une fois, vous n'aurez pas à le télécharger à nouveau.

C'est un système incroyable - à moins que vous n'essayiez de trouver la réponse à la question de savoir comment vider le cache.

Les modules sont chargés en utilisant une conception comme<script type="module" src="index.mjs"></script>. Le hachage du nom de fichier n'est pas utilisé. Comment, dans cette situation, est-il censé informer le navigateur de l'endroit où le télécharger index.mjs- à partir du cache ou du réseau?

Il convient de noter qu'il est presque possible de rendre le cache acceptable, mais cela prendra un certain effort. Voici ce dont vous avez besoin pour cela:

  • Vous devez définir le titre de toutes les réponses cache-controlsur une valeur no-cache. Incroyablement, pas de cache ne signifie pas «ne pas mettre en cache». Cela signifie que le fichier doit être mis en cache, mais le fichier ne doit pas être «utilisé pour satisfaire une demande ultérieure sans vérification réussie sur le serveur source».
  • ETag. — () , , , . , 38fd9e .
  • CDN- , . ETag . . CDN- . ( ETag) . (, , Firebase).
  • -, , « » . , , , , .

Par conséquent, lorsqu'un visiteur revisite le site, le navigateur dit: «Je dois télécharger le fichier index.mjs. Je vois que ce fichier existe déjà dans mon cache, son ETag l'est 38fd9e. Je vais demander ce fichier au serveur, mais je lui dirai de me l'envoyer uniquement si son ETag ne l'est pas 38fd9e. " Le technicien interceptera cette demande, ignorera ETag et la renverra index.mjsde son cache (ce fichier est entré dans le cache lors du dernier chargement de la page). L'agent de service redirige ensuite la demande vers le serveur. Le serveur renverra soit un message indiquant que le fichier n'a pas changé, soit un fichier qui sera stocké dans le cache.

À mon avis, tout cela est très gênant.

Maintenant, si vous êtes intéressé, le code du technicien de service:

self.addEventListener('fetch', e => {
  e.respondWith(
    (async () => {
      //       
      const cacheResponse = await caches.match(e.request);
      
      //   (),   
      // (ETag-    )
      fetch(e.request).then(fetchResponse => {
        caches
          .open(CACHE_NAME)
          .then(cache => cache.put(e.request, fetchResponse));
      });
      
      if (cacheResponse) return cacheResponse;
      
      return fetch(e.request);
    })()
  );
});

J'étais paresseux, donc je n'ai pas étudié et n'ai pas utilisé la dernière propriété FetchEvent.navigationPreload . Le fait est qu'à ce moment-là, j'ai passé plus de temps à mettre en cache qu'à écrire une application (pour votre information, j'ai passé 10 et 11 heures sur ces questions, respectivement).

Oui, je tiens à noter que la proposition de «carte d'importation» vise à résoudre certains des problèmes décrits ci-dessus. Il vous permet d'organiser quelque chose comme le mappage index.jssur index.38fd9e.mjs. Mais pour générer un hachage, de toute façon, vous avez besoin d'une sorte de pipeline d'assemblage, les cartes d'importation devront être incorporées dans le fichier HTML. Cela signifie qu'un bundler est nécessaire ici ... En fait, dans cette situation, les modules ne sont plus nécessaires dans le navigateur.

En conséquence, bien qu'il soit intéressant de comprendre tout cela, cela peut être comparé à la façon dont j'ai conduit partout sur un monocycle pendant une année entière. Je ne ferai plus ça.

Mais probablement tout le monde n'utilise pas de bundlers?


J'ai écrit ce matériel en supposant que tout le monde écrit du code dans des modules, en utilisant des constructions import/exportou require, puis en collectant le code dans des bundles de production en utilisant Webpack, Grunt, Gulp ou quelque chose comme ça.

Y a-t-il des développeurs qui n'utilisent pas de bundles? Y a-t-il quelqu'un qui place son code JavaScript dans plusieurs fichiers et l'envoie en production sans regroupement? Peut-être que l'une de ces personnes est toi? Si oui, je voudrais tout savoir sur ta vie.

Sommaire


Je m'efforce de prendre toutes les décisions sérieuses, telles que le choix entre les modules et les bundlers, sur la base des grands principes d'un développement efficace. C'est la productivité et la qualité.

Malheureusement, l'utilisation de modules dans les navigateurs ne contribue ni à l'un ni à l'autre.

Si demain je commence à travailler sur un nouveau projet, alors dans ma tête je n'aurai pas de question sur l'opportunité d'exécuter la bonne vieille application create-react. Tous les paramètres initiaux prendront environ trente secondes, et bien que la taille initiale du projet de 40 Ko soit un peu grande, pour la plupart des sites, cela ne jouera aucun rôle.

Et voici une situation différente. Supposons que je devrais rassembler un peu de HTML / CSS et JavaScript pour une sorte d'expérience, et une telle expérience serait un projet qui inclurait un peu plus de fichiers que «quelques-uns». Si, en travaillant sur ce projet, je n'avais pas prévu de passer du temps à configurer le système pour le construire, alors j'utiliserais probablement les modules dans le navigateur. Et puis - si je ne me souciais pas de la performance de ce projet.

Je serais intéressé de savoir comment Webpack et ses outils associés fonctionnent avec les modules ES6 dans un navigateur. Je pense qu'à l'avenir, lorsque la proposition de «cartes d'importation» recevra un soutien suffisant, les bundlers les utiliseront probablement comme un mécanisme pour résumer les hachages disgracieux qui sont utilisés aujourd'hui.

Chers lecteurs! Avez-vous utilisé des modules ES6 dans votre navigateur?


All Articles