38 votes

Étant donné ES2015, l'injection de dépendance et l'abstraction de bibliothèque, à quoi devrait ressembler mon module idéal en 2016 ?

Si ce n'était pas le cas, pour une chose, je serais tout à fait d'accord pour écrire tous mes modules comme

import A from './a.js';

var B = function(){
  //utiliser A
};

export default B;

puis utiliser un compilateur pour construire cela dans un format de navigateur ou de serveur.

Cependant, mon seul problème avec ce qui précède est la spécification explicite de ./a.js dans l'import.

Je comprends pourquoi la spécification a choisi cette voie1, pour favoriser l'analyse statique. Mais il y a deux raisons très pratiques pour lesquelles intégrer à la fois le nom de fichier et son chemin dans un module sont problématiques.

  1. Comme déjà mentionné ici, lorsque vous recyclez fréquemment des modules d'un projet à un autre, il est fort probable que vous ne puissiez pas maintenir un chemin cohérent vers cette ressource dans votre arborescence de projet. Intégrer un appel d'importation comme import myModule from './../../vendor/lib/dist/mod.js' dans le code d'un module ne me semble pas exactement une solution pérenne.
  2. En plus du chemin lui-même, spécifier le nom de fichier vous attache également. Quelque chose comme ceci semble innocent :

    import $ from 'vendor/jquery.js'

    Mais que se passera-t-il le jour où je voudrai utiliser Zepto au lieu de jQuery? J'ai trouvé l'abstraction, en particulier autour des bibliothèques de fournisseurs, extrêmement utile lors de la manipulation de bases de code volumineuses, de développeurs avisés et d'un écosystème JavaScript en évolution constante. Je voudrais peut-être importer React en tant que ma bibliothèque de composants aujourd'hui, mais qu'en est-il de demain ? De plus, que se passe-t-il si je vais utiliser le même module à la fois côté client et côté serveur, mais j'ai besoin de versions différentes d'une bibliothèque dépendante?

Je tiens à une abstraction robuste (mais claire et cohérente) dans mes équipes. Souvent, l'abstraction a pris la forme d'une sorte de nommage. J'ai un peu fantasmé à ce sujet :

//MAUVAIS: Incorporation de React dans mes modules de composants
import ComponentLib from './React.js';

//BON : Me laisse libre d'utiliser n'importe quelle bibliothèque similaire à React
import ComponentLib from 'vendor.lib.component';

vendor.lib.component, à la manière de Java, a été enregistré quelque part auparavant.

À noter que, contrairement à cette question, mon objectif n'est pas d'avoir un contrôle dynamique sur mes importations. Je ne veux pas de flexibilité à l'exécution, j'aimerais avoir de la flexibilité à la construction. Je devrais pouvoir remplacer un framework dépendant par un autre, ou par un faux, ou par quelque chose qui fonctionnera dans un environnement particulier, sans devoir m'inquiéter des dépendances que mes modules appellent, ou essayer de dupliquer un arbre de répertoires fou pour chaque produit de construction que je recherche.

Des questions similaires ont conduit à la suggestion d'une bibliothèque qui tire parti de la spécification Système, comme SystemJS. Vous pouvez ensuite utiliser quelque chose comme jspm pour introduire une carte de modules pour obtenir une abstraction. Mais au moment où je fais cela, j'écris tous mes modules différemment :

System.import('a', function(A){
  //utiliser 'A'
});

Est-ce soudainement l'avenir ? Si c'est le cas, pourquoi ne devrais-je pas simplement continuer à utiliser AMD ? Pourquoi même se donner la peine d'utiliser des modules ES2015 et d'exécuter des transpilateurs si je vais juste revenir à utiliser une API de chargement qui ressemble à de l'asynchrone ?

Encore plus décourageant, je ne vois pas beaucoup ou pas du tout de mention de l'attaque d'une norme d'API de chargeur de modules dans la spec ES2017.

(EDIT : Question révisée pour répondre aux normes d'une réponse non basée sur l'opinion)

Étant donné tout ce qui précède, je demande à la communauté -- comment puis-je écrire un module JavaScript qui (i) respecte la norme ES2015, (ii) ne fait pas référence à un module dépendant par son nom de fichier ou son chemin, et (iii) ne dépend pas d'outils/configuration intermédiaires étendus qui rendraient le partage du module avec plusieurs équipes prohibitif.

--

Note 1 Comme l'a noté @zeroflagL dans les commentaires, la spécification ne précise pas explicitement qu'un module doit être spécifié comme un chemin, juste une chaîne (voir ModuleSpecifier - http://www.ecma-international.org/ecma-262/6.0/#table-41). Cependant, il y a aussi une instruction claire pour prendre en compte les références circulaires, impliquant une sorte d'analyse statique (http://www.ecma-international.org/ecma-262/6.0/#sec-imports), les chemins de fichiers semblant être le contexte de référence privilégié jusqu'à présent. Nous ne pouvons donc pas blâmer la spécification d'être rigide ici, plutôt le contraire. Il incombe alors au reste d'entre nous de développer des implémentations plus robustes de import/ModuleSpecifier qui mènent à une norme secondaire.

3 votes

Cela semble être un sujet intéressant, mais ce n'est pas adapté à Stack Overflow, car il n'y a pas de réponse unique correcte.

0 votes

Vous pouvez personnaliser/augmenter le chargeur pour faire ce que vous voulez.

0 votes

@ Felix, il peut ne pas y avoir de réponse correcte, mais je pense qu'il y a une meilleure réponse. @ torazaburo, je le sais, mais ma question ne porte pas sur la capacité, elle concerne les normes. Pourquoi écrirais-je des modules que j'aimerais que d'autres utilisent basés sur une implémentation de chargeur folle que seule moi utilise?

1voto

Gaafar Points 682

Pour moi, il semble que ce soit l'un des plus grands problèmes non résolus de la communauté JS. Il n'y a pas de meilleures pratiques en matière de gestion des dépendances et d'injection de dépendances (du moins à ma connaissance).

Il y a une longue discussion sur ce fil : Dois-je avoir une injection de dépendance dans NodeJS, ou comment gérer ... ?, mais la plupart des solutions semblent fonctionner uniquement pour des cas spécifiques, et nécessitent de changer la façon dont vous écrivez des modules d'une certaine manière. Et ce qui est le plus choquant, c'est que de nombreuses réponses soutiennent que vous n'avez même pas besoin de DI.

Ma propre solution pour ce problème est ce cadre minimal d'injection de dépendances, qui vous permet de définir des modules une fois, et il les rassemblera pour vous avec les dépendances appropriées.

0voto

Borre Points 2656

Je sais que tu cherches une solution que tu peux utiliser spécifiquement en cas de compilateur JS, mais étant donné que tu cherches une meilleure pratique, je sens que nous devons prendre en considération le champ de jeu complet dans lequel se situe la gestion des dépendances JavaScript, y compris les différents environnements hôtes possibles comme le navigateur et Node.js, les systèmes d'emballage côté front-end comme Webpack, Browserify et jspm, et dans une certaine mesure les technologies voisines comme HTML et HTTP/HTTP2.

Histoire des modules ES

Il y a une raison pour laquelle tu ne trouveras pas d'API de chargement dans une quelconque spécification ECMA : la résolution des dépendances n'était pas finalisée lorsque la date limite de la spécification ECMA2015 est arrivée, et il a été décidé que la spécification ECMA ne décrirait que la syntaxe des modules, et que l'environnement hôte (par exemple, le navigateur, Node.js, le compilateur/transpileur JS) serait responsable de résoudre les modules à travers leurs spécificateurs, via un hook appelé HostResolveImportedModule, que tu pourras trouver dans les spécifications ECMA2015 et ECMA2017.

La spécification des sémantiques derrière le modèle de module ES2015 est maintenant entre les mains du WHATWG. Deux développements notables ont été le fruit de leurs efforts :

  1. Ils ont déterminé que les modules en HTML pouvaient être désignés par </code>.</li> <li>Il y a un <a href="https://whatwg.github.io/loader/" rel="nofollow noreferrer">brouillon pour une spécification de chargeur</a>, qui prendra en compte les différents environnements hôtes et systèmes d'emballage côté front-end. Cela ressemble à la spécification qui finira par aider à répondre à ta question, mais malheureusement elle n'est pas encore terminée et n'a pas été mise à jour depuis un certain temps.</li> </ol> <p><strong>Implémentations actuelles</strong></p> <p><em>Navigateur</em></p> <p>Avec l'ajout de la balise <code><script type="module"></code>, tout semble en place pour une implémentation simpliste des modules ES6 dans le navigateur. Actuellement, cela fonctionne sans aucune considération pour les performances (voir <a href="https://blog.whatwg.org/js-modules#comment-162662" rel="nofollow noreferrer">ceci</a> et <a href="https://blog.whatwg.org/js-modules#comment-162663" rel="nofollow noreferrer">cela</a>). De plus, cela ne prend pas en charge les systèmes d'emballage côté front-end. Il est clair que le concept de modules doit être étendu pour qu'il soit utilisable sur des sites Web de production, et donc les fournisseurs de navigateurs n'ont pas fait preuve de précipitation pour le mettre en œuvre dans leurs navigateurs.</p> <p><em>Node.js</em></p> <p>Node.js est une histoire complètement différente, car il a implémenté les modules style CommonJS depuis le début. Actuellement, il n'y a pas de support pour les modules ES2015. Deux suggestions distinctes ont été faites pour incorporer les modules ES6 dans Node.js aux côtés des modules CommonJS. <a href="https://nodesource.com/blog/es-modules-and-node-js-hard-choices/" rel="nofollow noreferrer">Cet article de blog</a> discute en détail ces suggestions.</p> <p><em>Compilateurs et systèmes d'emballage</em></p> <ul> <li>Webpack 2 <a href="https://webpack.js.org/concepts/modules/" rel="nofollow noreferrer">prend en charge les modules ES2015</a>. De plus, il peut être ajusté pour utiliser une <a href="http://moduscreate.com/es6-es2015-import-no-relative-path-webpack/" rel="nofollow noreferrer">résolution de dépendances personnalisée</a>.</li> <li><a href="https://babeljs.io/" rel="nofollow noreferrer">Babel</a> prend en charge les modules ES2015 et a la possibilité de les convertir en modules AMD, CommonJS, SystemJS et UMD. Il semble que les gestionnaires de paquets autres que Webpack prennent en charge les modules ES2015 via Babel.</li> </ul> <p><strong>Conclusion</strong></p> <p>Donc quand tu demandes le <em>meilleur moyen</em> d'écrire des modules, tu vois que c'est très difficile. Trouver une solution qui est compatible avec tous les environnements hôtes possibles est préférable, car :</p> <ul> <li> Tu pourrais vouloir partager ton code entre différents environnements.</li> <li> Il y a un risque qu'une classe de développeurs (par exemple, les développeurs front-end) adopteraient les modules ES2015, tandis que d'autres (par exemple, les développeurs Node.js) opteraient pour une autre solution (par exemple, les modules CommonJS). Cela réduirait encore davantage la possibilité de code inter-environnement.</li> </ul> <p> Mais tu verras ci-dessus que les différents environnements ont des exigences différentes. Dans ce sens, la meilleure réponse possible à ta question pourrait être : il n'y a actuellement aucun <em>meilleur moyen</em> d'écrire des modules qui couvre tes préoccupations d'abstraction.</p> <p><strong>Solution</strong></p> <p> Cependant, si j'avais la tâche en ce moment d'écrire des modules ES2015 qui se compilent en JS, je resterais à l'écart des chemins relatifs et utiliserais toujours des chemins absolus depuis la racine du projet comme identifiant des modules, ce que je ne considère pas comme problématique. En fait, Java reflète les espaces de noms et sa structure de répertoire de manière similaire. J'utiliserais Webpack ou Babel pour compiler mon code source en code exécutable dans les environnements JS d'aujourd'hui.</p> <p> En ce qui concerne ton autre problème, si je voulais être capable de substituer les bibliothèques de fournisseurs, je créerais probablement un module qui fait l'alias des bibliothèques de fournisseurs vers des noms que j'utiliserais en interne. Quelque chose comme :</p> <pre><code>// /lib/libs.js import jQuery from 'vendor/jquery.js' export const $ = jQuery; </code></pre> <p> Tous les autres modules importeraient alors $ depuis lib/libs.js et tu pourrais remplacer les bibliothèques de fournisseurs en changeant la référence en un seul endroit.</p></x-turndown>

-1voto

Pawel Points 204

Si vous voulez suivre les meilleures pratiques, suivez le guide de style JavaScript AirBnb. À mon avis, le guide de style JavaScript le meilleur et le plus complet disponible

https://github.com/airbnb/javascript#classes--constructors

Import

Cela semble mauvais pour la réutilisation des modules : import myModule from './../../vendor/lib/dist/mod.js'

Publiez votre module sur NPM (qui peut également être privé ou sur un NPM auto-hébergé) et importez comme ça import myModule from 'my-module';

Définissez éventuellement votre NODE_PATH en tant que dossier racine et faites référence aux modules de manière relative à la racine.

Dans package.json

'start': 'NODE_PATH=. node index.js'

// sous Windows
'start': 'NODE_PATH=. && node index.js'

Importez maintenant comme ça:

import myModule from 'vendor/lib/dist/mod.js'

Variables

var ne fait pas partie d'ES6. Utilisez:

  • constante - lorsque la valeur de la variable ne changera pas, ainsi que pour les objets et les imports. Même si les paramètres de l'objet changent, c'est toujours une constante.

  • let - lorsque la valeur de la variable change, par exemple for(let = i; i < 100; i++)

  • D'après mon expérience personnelle, définissez toujours const comme valeur par défaut et ne passez à let que si ESLint se plaint (au fait, utilisez ESLint http://eslint.org/)

Classes

Il y a maintenant une manière correcte de définir des classes en JavaScript

class B {
  constructor() {
  }
  doSomething() {
  }
}

Votre exemple mis à jour:

import A from './a';

Class B {
  constructor() {
  }
  doSomething() {
  }
};

export default B;

Si vous voulez étendre A:

import A from './a';

Class B extends A{
  constructor(argumentA, argumentB) {
    super(argumentA, argumentB);
    this.paramA = argumentA;
  }
};

export default B;

Astuces

  • Utilisez Webpack avec NPM comme outil de construction. N'utilisez pas Gulp ou Grunt
  • Utilisez Babel pour transpiler votre code (le chargeur JSX peut ne pas être suffisant)
  • Apprenez à ne pas utiliser du tout jQuery, mais choisissez plutôt les bons polyfills et outils pour les tâches que vous devez effectuer à partir de NPM
  • Il existe des tonnes de dépôts de modèles bien écrits sur github, alors volez chez les meilleurs. Voici quelques-uns que j'utilise pour React.

Ma réponse en essence est:

Le modèle/bibliothèque que vous avez demandé est le guide de style JavaScript AirBnb et oubliez jQuery

2 votes

" var n'est pas partie de l'ES6 " - quoi?

0 votes

Je ne pense pas que l'auteur original voulait dire A et B comme des classes, donc tes exemples ne sont pas vraiment pertinents.

0 votes

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X