Passer au contenu principal




Découvrez l'impact des modules CommonJS sur le changement de votre arborescence d'applications


Mise à jour

Il apparaît dans:
Temps de chargement rapides

En esta publicación, veremos qué es CommonJS y por qué hace que sus paquetes de JavaScript sean más grandes de lo necesario.

Résumé: Para asegurarse de que el empaquetador pueda optimiser con éxito su aplicación, evite depender de los módulos CommonJS y utilice la sintaxis del módulo ECMAScript en toda su aplicación.

Qu'est-ce que CommonJS?

CommonJS es un estándar de 2009 que estableció convenciones para módulos JavaScript. Inicialmente estaba destinado a ser utilizado fuera del le navigateur la toile, principalmente para aplicaciones del lado del serveur.

Avec CommonJS, vous pouvez définir des modules, exporter leurs fonctionnalités et les importer dans d'autres modules. Par exemple, l'extrait ci-dessous définit un module qui exporte cinq fonctions: ajouter, soustraire, multiplier, divisery max:


const { maxBy } = exiger('lodash-es');
const fns = {
ajouter: (à, b) => à + b,
soustraire: (à, b) => à - b,
multiplier: (à, b) => à * b,
diviser: (à, b) => à / b,
max: arr => maxBy(arr)
};

Object.keys(fns).forEach(fnName => module.exportations[fnName] = fns[fnName]);

Plus tard, un autre module peut importer et utiliser tout ou partie de ces fonctions:


const { ajouter } = exiger('./utils');
console.Journal(ajouter(1, 2));

Invoquer indice.js avec ne donnent pas affichera le numéro 3 sur la console.

Debido a la falta de un sistema de módulos estandarizado en el navegador a principios de la década de 2010, CommonJS también se convirtió en un formato de módulo popular para las bibliotecas del lado del client de JavaScript.

Comment CommonJS affecte-t-il la taille finale de votre colis?

La taille de votre application JavaScript côté serveur n'est pas aussi critique que dans le navigateur, c'est pourquoi CommonJS n'a pas été conçu pour réduire la taille du package de production. En même temps, Analyse montre que la taille des paquets JavaScript est toujours la principale raison du ralentissement des applications du navigateur.

Groupeurs et minificateurs JavaScript, tels que Webpack y terser, realice diferentes optimizaciones para reducir el tamaño de su aplicación. Al analizar su aplicación en el momento de la compilación, intentan eliminar tanto como être posible del code source que no está utilizando.

Par exemple, dans l'extrait de code ci-dessus, votre package final ne doit inclure que le ajouter fonction car c'est le seul symbole de utils.js dans quoi est-ce que tu as de l'importance index.js.

Construisons l'application en utilisant ce qui suit Webpack réglage:

const path = exiger('path');
module.exportations = {
entry: 'index.js',
output: {
filename: 'out.js',
path: path.resolve(__dirname, 'dist'),
},
mode: 'production',
};

Ici, nous spécifions que nous voulons utiliser les optimisations du mode production et utiliser index.js comme point d'entrée. Après avoir invoqué Webpack, si nous explorons le sortir taille, nous verrons quelque chose comme ceci:

$ CD dist && ls -lah
625K Apr 13 13:04 out.js

Se rendre compte de le paquet est 625KB. Si nous regardons la sortie, nous trouverons toutes les fonctions de utils.js plus de nombreux modules de lodash. Bien que nous n'utilisions pas lodash au index.js fait partie de la sortie, ce qui ajoute beaucoup de poids supplémentaire à nos actifs de production.

Modifions maintenant le format du module en Modules ECMAScript et essayez à nouveau. Cette fois, utils.js cela ressemblerait à ceci:

exportation const ajouter = (à, b) => à + b;
exportation const soustraire = (à, b) => à - b;
exportation const multiplier = (à, b) => à * b;
exportation const diviser = (à, b) => à / b;

importer { maxBy } desde 'lodash-es';

exportation const max = arr => maxBy(arr);

Oui index.js dérangerait utils.js en utilisant la syntaxe du module ECMAScript:

importer { ajouter } desde './utils';

console.Journal(ajouter(1, 2));

En utilisant le même Webpack configuration, nous pouvons construire notre application et ouvrir le fichier de sortie. Maintenant c'est 40 octets avec ce qui suit sortir:

(()=>{"use strict";console.Journal(1+2)})();

Notez que le package final ne contient aucune des fonctions de utils.js que nous n'utilisons pas, et il n'y a aucune trace de lodash! Encore plus, terser (le minificateur JavaScript qui Webpack utilise) dans le ajouter courir console.log.

Une bonne question que vous pourriez poser est: Pourquoi l'utilisation de CommonJS rend le paquet de sortie presque 16 000 fois plus volumineux?? Bien sûr, il s'agit d'un exemple de jouet, en fait, la différence de taille n'est peut-être pas si grande, mais CommonJS ajoutera probablement un poids significatif à votre production.

Les modules CommonJS sont plus difficiles à optimiser dans le cas général car ils sont beaucoup plus dynamiques que les modules ES. Pour vous assurer que votre stitcher et votre minificateur peuvent optimiser avec succès votre application, évitez de vous fier aux modules CommonJS et utilisez la syntaxe du module ECMAScript dans toute votre application.

Notez que même si vous utilisez des modules ECMAScript dans index.jsSi le module que vous utilisez est un module CommonJS, la taille du package de votre application sera affectée.

Pourquoi CommonJS élargit-il son application?

Pour répondre à cette question, nous examinerons le comportement de ModuleConcaténationPlugin au Webpack y, después de eso, discutir la analizabilidad estática. Este complemento concatena el portée de todos sus módulos en un solo cierre y permite que su código tenga un tiempo de ejecución más rápido en el navegador. Veamos un ejemplo:


exportation const ajouter = (à, b) => à + b;
exportation const soustraire = (à, b) => à - b;


importer { ajouter } desde './utils';
const soustraire = (à, b) => à - b;

console.Journal(ajouter(1, 2));

Ci-dessus, nous avons un module ECMAScript, que nous importons dans index.js. Nous définissons également un soustraire une fonction. Nous pouvons construire le projet en utilisant le même Webpack configuration comme ci-dessus, mais cette fois, nous désactiverons la minimisation:

const path = exiger('path');

module.exportations = {
entry: 'index.js',
output: {
filename: 'out.js',
path: path.resolve(__dirname, 'dist'),
},
optimization: {
minimize: faux
},
mode: 'production',
};

Voyons la sortie produite:

 (() => { 
"use strict";


const ajouter = (à, b) => à + b;
const soustraire = (à, b) => à - b;


const index_subtract = (à, b) => à - b;**
console.Journal(ajouter(1, 2));**

})();

Dans la sortie ci-dessus, toutes les fonctions se trouvent dans le même espace de noms. Pour éviter les collisions, le webpack a été renommé soustraire courir index.js à index_subtract.

Si un minificateur traite le code source ci-dessus, il effectuera les opérations suivantes:

  • Supprimer les fonctions inutilisées soustraire y index_subtract
  • Supprimez tous les commentaires et les espaces redondants
  • Inclinez le corps du ajouter fonction dans le console.log appel

Ceci est souvent mentionné par les développeurs suppression des importations inutilisées telles que le tremblement des arbres. La secousse d'arbre n'était possible que parce que le package Web était capable de comprendre statiquement (au moment de la compilation) quels symboles nous importions utils.js et quels symboles il exporte.

Ce comportement est activé par défaut pour Modules ES Parce qu'ils sont plus analysables statiquement, par rapport à CommonJS.

Regardons exactement le même exemple, mais cette fois, le changement utils.js pour utiliser CommonJS au lieu des modules ES:


const { maxBy } = exiger('lodash-es');

const fns = {
ajouter: (à, b) => à + b,
soustraire: (à, b) => à - b,
multiplier: (à, b) => à * b,
diviser: (à, b) => à / b,
max: arr => maxBy(arr)
};

Object.keys(fns).forEach(fnName => module.exportations[fnName] = fns[fnName]);

Cette petite mise à jour changera considérablement la sortie. Comme il est trop long à insérer sur cette page, je n'ai partagé qu'une petite partie:

...
(() => {

"use strict";
var _utils__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(288);
const soustraire = (à, b) => à - b;
console.Journal((0,_utils__WEBPACK_IMPORTED_MODULE_0__ .IH)(1, 2));

})();

Notez que le package final contient des Webpack "Runtime": code injecté responsable de l'importation / exportation des fonctionnalités des modules emballés. Cette fois, au lieu de placer tous les symboles de utils.js y index.js sous le même espace de noms, nous demandons dynamiquement, au moment de l'exécution, le ajouter fonction utilisant __webpack_require__.

Ceci est nécessaire car avec CommonJS, nous pouvons obtenir le nom de l'exportation à partir d'une expression arbitraire. Par exemple, le code suivant est une construction absolument valide:

module.exportations[localStorage.getItem(Math.random())] = () => {};

No hay forma de que el empaquetador sepa en el tiempo de compilación cuál es el nombre del símbolo exportado, ya que esto requiere información que solo está disponible en tiempo de ejecución, en el contexto del navegador del Nom d'utilisateur.

De cette façon, le minificateur est incapable de comprendre ce qu'est exactement index.js utilise ses dépendances afin que vous ne puissiez pas vous en débarrasser. Nous observerons également exactement le même comportement pour les modules tiers. Si nous importons un module CommonJS depuis node_modules, votre chaîne d'outils de construction ne pourra pas l'optimiser correctement.

Secouez les arbres avec CommonJS

Les modules CommonJS sont beaucoup plus difficiles à analyser car ils sont dynamiques par définition. Par exemple, l'emplacement d'importation dans les modules ES est toujours un littéral de chaîne, par rapport à CommonJS, où il s'agit d'une expression.

Dans certains cas, si la bibliothèque que vous utilisez suit des conventions spécifiques sur la façon dont elle utilise CommonJS, il est possible de supprimer les exportations inutilisées au moment de la construction en utilisant un tiers Webpack prise de courant. Bien que ce plugin ajoute la prise en charge de la secousse d'arbre, il ne couvre pas toutes les différentes façons dont vos dépendances pourraient utiliser CommonJS. Cela signifie que vous n'obtenez pas les mêmes garanties qu'avec les modules ES. Ajoutez également un coût supplémentaire dans le cadre de votre processus de construction en plus de la valeur par défaut Webpack comportement.

conclusion

Pour vous assurer que le conditionneur peut optimiser avec succès votre application, évitez de vous fier aux modules CommonJS et utilisez la syntaxe du module ECMAScript dans toute votre application.

Voici quelques conseils pratiques pour vérifier que vous êtes sur le chemin optimal:

  • Utilisez Rollup.js résolution nodejs
    brancher et définissez le modulesSeulement coche pour indiquer que vous souhaitez dépendre uniquement des modules ECMAScript.
  • Utilisez le package est-esm

    pour vérifier qu'un package npm utilise des modules ECMAScript.

  • Si vous utilisez Angular, par défaut, vous recevrez un avertissement si vous comptez sur des modules qui ne peuvent pas être ébranlés.