Objectif des externes
Les externs sont des déclarations qui indiquent au compilateur Closure les noms des symboles qui ne doivent pas être renommés lors de la compilation avancée. Ils sont appelés "externes" car ces symboles sont le plus souvent définis par du code en dehors de la compilation, tel que du code natif ou des bibliothèques tierces. Pour cette raison, les fichiers externes comportent souvent des annotations de type, afin que Closure Compiler puisse vérifier le type de votre utilisation de ces symboles.
En général, il est préférable de considérer les fichiers externes comme un contrat d'API entre l'implémenteur et les consommateurs d'un code compilé. Les externs définissent ce que l'implémenteur s'engage à fournir et ce sur quoi les consommateurs peuvent s'appuyer. Les deux parties doivent disposer d'une copie du contrat.
Les externs sont semblables aux fichiers d'en-tête dans d'autres langages.
Syntaxe des externs
Les fichiers externes ressemblent beaucoup à des fichiers JavaScript normaux annotés pour Closure Compiler. La principale différence est que leur contenu n'est jamais imprimé dans la sortie compilée. Par conséquent, aucune des valeurs n'a de sens, seuls les noms et les types en ont.
Vous trouverez ci-dessous un exemple de fichier externs pour une bibliothèque simple.
// The `@externs` annotation is the best way to indicate a file contains externs. /** * @fileoverview Public API of my_math.js. * @externs */ // Externs often declare global namespaces. const myMath = {}; // Externs can declare functions, most importantly their names. /** * @param {number} x * @param {number} y * @return {!myMath.DivResult} */ myMath.div = function(x, y) {}; // Note the empty body. // Externs can contain type declarations, such as classes and interfaces. /** The result of an integer division. */ myMath.DivResult = class { // Constructors are special; member fields can be declared in their bodies. constructor() { /** @type {number} */ this.quotient; /** @type {number} */ this.remainder; } // Methods can be declared as usual; their bodies are meaningless though. /** @return {!Array<number>} */ toPair() {} }; // Fields and methods can also be declared using prototype notation. /** * @override * @param {number=} radix */ myMath.DivResult.prototype.toString = function(radix) {};
Option --externs
En général, l'annotation @externs
est le meilleur moyen d'indiquer au compilateur qu'un fichier contient des externs. Ces fichiers peuvent être inclus en tant que fichiers sources normaux à l'aide de l'indicateur de ligne de commande --js
.
Toutefois, il existe une autre méthode plus ancienne pour spécifier les fichiers externes. L'option de ligne de commande --externs
peut être utilisée pour transmettre explicitement les fichiers externs. Cette méthode n'est pas recommandée.
Utiliser des externes
Les externs ci-dessus peuvent être utilisés comme suit.
/** * @fileoverview Do some math. */ /** * @param {number} x * @param {number} y * @return {number} */ export function greatestCommonDivisor(x, y) { while (y != 0) { const temp = y; // `myMath` is a global, it and `myMath.div` are never renamed. const result = myMath.div(x, y); // `remainder` is also never renamed on instances of `DivResult`. y = result.remainder; x = temp; } return x; }
Objectif des exportations
Les exportations sont un autre mécanisme permettant d'attribuer des noms cohérents aux symboles après la compilation. Elles sont moins utiles que les externs et souvent déroutantes. Il est préférable de les éviter dans tous les cas, sauf les plus simples.
Les exportations reposent sur le fait que Closure Compiler ne modifie pas les littéraux de chaîne. En attribuant un objet à une propriété nommée à l'aide d'un littéral, l'objet sera disponible via ce nom de propriété, même après la compilation.
Vous trouverez ci-dessous un exemple simple.
/** * @fileoverview Do some math. */ // Note that the concept of module exports is totally unrelated. /** @return {number} */ export function myFunction() { return 5; } // This assignment ensures `myFunctionAlias` will be a global alias exposing `myFunction`, // even after compilation. window['myFunctionAlias'] = myFunction;
Si vous utilisez la bibliothèque Closure, les exportations peuvent également être déclarées à l'aide des fonctions goog.exportSymbol
et goog.exportProperty
.
Pour en savoir plus, consultez la documentation de la bibliothèque Closure sur ces fonctions. Toutefois, sachez qu'ils bénéficient d'une prise en charge spéciale du compilateur et qu'ils seront totalement transformés dans le résultat compilé.
Problèmes liés aux exportations
Les exportations diffèrent des externs en ce qu'elles ne créent qu'un alias exposé que les consommateurs peuvent référencer. Dans le code compilé, le symbole exporté sera toujours renommé. C'est pourquoi les symboles exportés doivent être constants, car leur réaffectation dans votre code entraînerait la redirection de l'alias exposé vers le mauvais élément.
Cette subtilité de la modification du nom est particulièrement complexe en ce qui concerne les propriétés d'instance exportées.
En théorie, les exportations peuvent permettre une taille de code plus petite que les externs, car les noms longs peuvent toujours être remplacés par des noms plus courts dans votre code. En pratique, ces améliorations sont souvent très mineures et ne justifient pas la confusion que créent les exportations.
Les exportations ne fournissent pas non plus d'API à suivre pour les consommateurs, contrairement aux externs. Par rapport aux exportations, les externs documentent les symboles que vous souhaitez exposer, leurs types et vous permettent d'ajouter des informations sur leur utilisation. De plus, si vos consommateurs utilisent également Closure Compiler, ils auront besoin d'externs pour la compilation.