Written by

Le déve­lop­pe­ment Web fron­tend est deve­nu un par­cours du com­bat­tant au fil des ans. Il s’est com­plexi­fié aus­si bien du point de vue des outils que des fra­me­works, et vous avez for­cé­ment été confron­té au phé­no­mène de JavaScript Fatigue dû à la crois­sance rapide de tout l’écosystème.

Alors accro­chez-vous, en 2020, on repart en arrière ! L’avenir est déjà là, et il est plus que relui­sant 🤩 !

Au commencement était JavaScript…

… et puis tout s’est accé­lé­ré. Avec l’arrivée de Node.js, JS est deve­nu le nou­veau chal­len­ger. Être capable d’exécuter du JavaScript à la fois sur le backend et le fron­tend a consi­dé­ra­ble­ment sim­pli­fié le déve­lop­pe­ment de nom­breux outils, de nou­velles (petites) biblio­thèques et de nou­veaux envi­ron­ne­ments. Emporté par le Registre NPM, il s’est rapi­de­ment éten­du et est main­te­nant le lan­gage de fac­to pour tous les outils de déve­lop­pe­ment front, ain­si que pour la dis­tri­bu­tion des dépen­dances.

Puis sont appa­rus les fra­me­works front réac­tifs, comme React, Vue.js, ou Svelte. Avec leurs archi­tec­tures intel­li­gentes, ils consti­tuent les biblio­thèques UI les plus rapides à l’heure actuelle pour déve­lop­per des inter­faces com­plexes dans le navi­ga­teur.

Enfin, l’ère PWA. Nous déve­lop­pons main­te­nant des appli­ca­tions Web en uti­li­sant les tech­no­lo­gies Web, mais pas néces­sai­re­ment liées à des sites Web. Ce sont des appli­ca­tions, comme toutes les autres. Installables, elles fonc­tionnent dans des navi­ga­teurs chro­me­less comme Electron ou les WebViews, et n’ont rien à envier aux ver­sions natives.

Nous sommes en 2020, et nous pen­sons désor­mais en dehors du navi­ga­teur : JavaScript fonc­tionne par­tout, nous déve­lop­pons des appli­ca­tions en uti­li­sant les tech­no­lo­gies du web, et nous appli­quons les mêmes modèles que pour les déve­lop­pe­ments natifs. Tout cela avec les mêmes mises en garde : à mesure que l’écosystème JS se déve­loppe, les dépen­dances de nos appli­ca­tions croissent éga­le­ment. Nous sommes main­te­nant en train de com­pi­ler pour le Web. Comme pour des déve­lop­pe­ments natifs.

Il y a cepen­dant une petite, mais signi­fi­ca­tive, dif­fé­rence : nous dif­fu­sons nos appli­ca­tions en strea­ming. Lorsque vous ser­vez une appli­ca­tion web, vous uti­li­sez tou­jours les fon­da­men­taux du Web : votre ser­veur envoie une page HTML qui demande à son tour ses res­sources (images, JS, polices, CSS, etc.). Nous envoyons chaque jour des tonnes de Go aux navi­ga­teurs de nos uti­li­sa­teurs, et il est temps d’arrêter l’hémorragie ! En pre­nant le pro­blème dès la concep­tion.

Le péché originel : le packing

Aux pre­miers temps des appli­ca­tions Web, nous avions un nou­veau modèle pour dis­tri­buer des mor­ceaux de JavaScript à tra­vers les pro­jets sous forme de petites librai­ries : CommonJS. Il était si moderne qu’il est deve­nu la norme pour la dis­tri­bu­tion de modules dans l’environnement Node.js. Le prin­ci­pal pro­blème était l’incapacité de la VM de notre navi­ga­teur à com­prendre et à exploi­ter ce for­mat.

Nous avons donc com­men­cé à déve­lop­per des loa­ders, capables de char­ger et d’injecter des modules CommonJS dans nos pages prin­ci­pales. Mais cela a rapi­de­ment conduit à un autre pro­blème : nous avons com­men­cé à ser­vir trop de dépen­dances au navi­ga­teur, ce qui a fait gon­fler les coûts de connexion.

Nous avons donc créé des bund­lers, comme Webpack. Ils sont char­gés de col­lec­ter toutes les dépen­dances et les modules de votre appli­ca­tion et de pro­duire un seul fichier JS que votre navi­ga­teur peut exé­cu­ter. Ils sont rapi­de­ment deve­nus des packers en même temps, vous per­met­tant d’appliquer des chan­ge­ments et des opti­mi­sa­tions à vos builds, et d’ajouter des méca­nismes pour impor­ter bien plus que des dépen­dances JS !

Nous avons ensuite ajou­té des pré­pro­ces­seurs et des trans­pi­la­teurs, pour écrire avec de nou­veaux lan­gages vers JavaScript (comme CoffeeScript ou TypeScript), puis de nou­veaux outils pour des opti­mi­sa­tions sup­plé­men­taires, en divi­sant les res­sources en petits mor­ceaux et en les ser­vant uni­que­ment à la demande. Parce que la per­for­mance en pre­nait un sacré coup.

Notre outillage de déve­lop­pe­ment est main­te­nant plus com­pli­quée qu’il ne l’a jamais été. Nous sommes dans une course sans fin pour ajou­ter de nou­veaux outils qui nous per­mettent de faire face au pro­blème de la per­for­mance. Nous les avons créés parce que nous avions besoin de ces outils, mais notre chaîne de pro­duc­tion est main­te­nant un hor­rible pro­ces­sus bouf­fi, qui est extrê­me­ment com­plexe à com­prendre et qui prend une éter­ni­té à démar­rer et à com­pi­ler chaque fois que vous démar­rez un pro­jet ou enre­gis­trez un fichier.

Cette situa­tion est-elle encore bien légi­time ?

Bienvenue en 2020 : Zero Build Pipeline

Alors, pas­sons en revue la liste. Qu’avons-nous aujourd’hui dans notre boîte à outils ?

  1. Modules ES6 natifs : navré pour le for­mat CommonJS, mais son des­cen­dant, le Module ES6, est bien plus effi­cace. Il est sup­por­té en natif par Node.js et les navi­ga­teurs. Il n’y a plus de rai­son de l’éviter.
  2. Libres pré-com­pi­lées : TypeScript est en de plus en plus cou­ram­ment uti­li­sé 1) et nous avons beau­coup de nou­velles biblio­thèques qui sont déve­lop­pées avec ce lan­gage. Même déve­lop­pés en VanillaJS, ces modules peuvent être opti­mi­sés en dehors de notre chaîne d’outils et dis­tri­bués dans une ver­sion de pro­duc­tion. Pas besoin de gérer cela de notre côté dans notre pro­jet.
  3. Un grand éco­sys­tème : NPM est l’un des registres de dépen­dances les plus impor­tants au monde. Tout l’univers JavaScript/TypeScript est rem­pli d’excellents outils et de librai­ries utiles, prêts à être uti­li­sés sans devoir les embar­quer dans notre build final.
  4. HTTP/2 : La ver­sion 2 du pro­to­cole est lar­ge­ment dis­po­nible et sup­por­tée, et elle réduit un grand nombre de requêtes. En effet, il est pro­ba­ble­ment plus effi­cace de ser­vir un tas de petits fichiers avec HTTP/2 plu­tôt qu’un gros fichier unique, grâce aux opti­mi­sa­tions du pro­to­cole. Sans comp­ter le béné­fice asso­cié à la mise en cache.
  5. Tous nos outils, des com­mandes Unix aux appli­ca­tions dédiées au fron­tend, offrent une CLI, qui per­met la construc­tion de pipe­lines avan­cés.

Cette boîte à outils est donc riche en pos­si­bi­li­tés. Intuitivement, elle nous pousse vers le Bare Modules Devtools. Voici les idées simples qui se cachent der­rière le concept :

  1. Nous uti­li­sons les modules tels qu’ils sont dis­tri­bués, dans leur for­mat de module ES6, sans les empa­que­ter ou les regrou­per,
  2. Nous comp­tons sur HTTP/2 pour ser­vir effi­ca­ce­ment les fichiers de res­sources afin de main­te­nir un taux de per­for­mance éle­vé,
  3. Nous nous en tenons à 0ms pour démar­rer l’environnement de déve­lop­pe­ment, en ne ser­vant que les fichiers bruts et les modules et en ne les modi­fiant pas dans un contexte de déve­lop­pe­ment,
  4. Nous pré/­post-trai­tons nos fichiers sources en uti­li­sant direc­te­ment les CLI (ou avec des outils natifs) pour évi­ter de créer de nou­velles chaînes d’outils com­plexes.

Le 5 mai der­nier a vu le lan­ce­ment de la ver­sion 2 de Snowpack, et c’est exac­te­ment ce dont nous avions besoin pour mettre en œuvre ce concept ! Ce nou­vel outil est à la fois un outil de déve­lop­pe­ment, qui vous per­met d’exécuter vos appli­ca­tions en mode déve­lop­pe­ment sans temps de démar­rage, sans packing, avec des mises à jour rapides, et un outil de construc­tion où vous pou­vez défi­nir et asso­cier (pipe) des outils pour pré­pa­rer vos res­sources pour les ver­sions de pro­duc­tion si néces­saire.

Vous n’avez encore rien vu !

Commencez par ins­tal­ler Snowpack comme dépen­dance de votre pro­jet :


Et c’est tout ! Il est main­te­nant prêt à prendre en charge votre pro­ces­sus de déve­lop­pe­ment.

Outils de développement

Snowpack ana­ly­se­ra votre pro­jet, à la recherche de décla­ra­tions d’import, puis copie­ra les res­sources à par­tir du réper­toire node_modules du pro­jet, ou les ins­tal­le­ra direc­te­ment, dans un réper­toire web_modules.

De là, vous pou­vez ser­vir vos res­sources sous forme de module web et les impor­ter dans votre page :


Vos scripts doivent être décla­rés dans le HTML avec l’attribut type=module pour acti­ver le mode Module ES6. Et vos res­sources sont impor­tées depuis le réper­toire web_modules à la racine.

Il suf­fit de lan­cer l’installation de snow­pack depuis le réper­toire de votre pro­jet : le dos­sier web_modules va se peu­pler avec vos dépen­dances. Vous pou­vez éga­le­ment les gérer à par­tir de votre fichier package.json et avec les entrées de confi­gu­ra­tion de snow­pack.

En mode de déve­lop­pe­ment, cette confi­gu­ra­tion vous per­met de décla­rer des fonc­tion­na­li­tés utiles, comme les direc­tives de mon­tage. Elles agissent comme des points de mon­tage Unix, vous per­met­tant de ser­vir tous les élé­ments que vous sou­hai­tez à par­tir du ser­veur de déve­lop­pe­ment inté­gré :


Ensuite, il suf­fit de faire tour­ner snowpack dev pour obte­nir un ser­veur de déve­lop­pe­ment com­plet, ser­vant vos res­sources avec un LiveReload embar­qué. Avec la com­mande dev, il n’est même plus néces­saire de mettre à jour les che­mins d’accès vous-même : il suf­fit de suivre la syn­taxe habi­tuelle, comme import * as React from 'react' dans vos fichiers JS, et Snowpack conver­ti­ra le che­min d’accès à la volée.

Au moment du build, Snowpack copie sim­ple­ment vos fichiers, ce qui vous per­met de les déployer comme vous le sou­hai­tez. Il ajoute éga­le­ment une option --bundle, vous per­met­tant de pas­ser vos fichiers pro­duits à Parcel, pour opti­mi­ser vos ver­sions de pro­duc­tion en s’appuyant sur un éco­sys­tème déjà exis­tant.

Vous pou­vez éga­le­ment défi­nir vos propres scripts snow­pack, pour per­son­na­li­ser vos pipe­lines de construc­tion, comme :


L’ancien modèle n’est plus adap­té à nos contraintes modernes. Grâce aux tech­no­lo­gies actuel­le­ment dis­po­nibles, nous n’avons plus besoin de condi­tion­ner et de regrou­per nos appli­ca­tions avec leurs dépen­dances. Au lieu de cela, nous pou­vons nous concen­trer sur le déve­lop­pe­ment plu­tôt que sur l’outillage.

Cette approche pré­sente une bonne phi­lo­so­phie : elle s’appuie sur les outils exis­tants pour main­te­nir un effort de com­pa­ti­bi­li­té croi­sée (comme le tra­vail avec Parcel, Rollup, Babel… sous le capot), elle n’impose aucune contrainte et vous pou­vez vous rabattre sur n’importe quel autre outil de build si vous en avez besoin, et elle est rapide à exé­cu­ter.

Snowpack est le pre­mier concur­rent de ce nou­vel uni­vers, avec de nom­breuses fonc­tion­na­li­tés inté­res­santes, dont import-maps, de la proxi­fi­ca­tion de requêtes, du HMR, etc. Nul doute que nous ver­rons rapi­de­ment d’autres alter­na­tives 2), et c’est le bon moment pour sau­ter dans le train du bare modules deve­lop­ment !

Notes   [ + ]

1. Saviez-vous que vous pou­vez exé­cu­ter TypeScript sur les ser­veurs always­da­ta direc­te­ment grâce à Deno ?
2. Vue.js a déjà com­men­cé à tra­vailler sur son propre outil pour Vue.js : Vite.