Comment inclure un fichier JavaScript dans un autre fichier JavaScript ?

Existe-t-il en JavaScript quelque chose de similaire à @import en CSS qui vous permet d'inclure un fichier JavaScript dans un autre fichier JavaScript ?

Solution

Les anciennes versions de JavaScript n'avaient pas d'import, d'include ou de require, si bien que de nombreuses approches différentes de ce problème ont été développées.

Mais depuis 2015 (ES6), JavaScript dispose de la norme ES6 modules pour importer des modules dans Node.js, qui est également prise en charge par la plupart des navigateurs modernes.

Pour assurer la compatibilité avec les navigateurs plus anciens, il est possible d'utiliser les outils build et/ou transpilation.

Modules ES6

Les modules ECMAScript (ES6) sont supportés par Node.js depuis la version 8.5, avec le drapeau --experimental-modules. Tous les fichiers impliqués doivent avoir l'extension .mjs.

// module.mjs
export function hello() {
  return "Hello";
}
// main.mjs
import { hello } from 'module'; // or './module'
let val = hello();  // val is "Hello";

Modules ECMAScript dans les navigateurs

Les navigateurs prennent en charge le chargement direct des modules ECMAScript (sans outils comme Webpack) depuis Safari 10.1, Chrome 61, Firefox 60 et Edge 16. Consultez la prise en charge actuelle sur caniuse.

<script type="module">
  import { hello } from './hello.mjs';
  hello('world');
</script>
// hello.mjs
export function hello(text) {
  const div = document.createElement('div');
  div.textContent = `Hello ${text}`;
  document.body.appendChild(div);
}

Plus d'informations sur https://jakearchibald.com/2017/es-modules-in-browsers/

Importations dynamiques dans les navigateurs

Les importations dynamiques permettent au script de charger d'autres scripts selon les besoins :

<script type="module">
  import('hello.mjs').then(module => {
      module.hello('world');
    });
</script>

Plus d'informations sur https://developers.google.com/web/updates/2017/11/dynamic-import

Node.js require

L'ancien style d'importation de modules, encore largement utilisé dans Node.js, est le système module.exports/require.

// mymodule.js
module.exports = {
   hello: function() {
      return "Hello";
   }
}
// server.js
const myModule = require('./mymodule');
let val = myModule.hello(); // val is "Hello"   

Il existe d'autres moyens pour JavaScript d'inclure des contenus JavaScript externes dans les navigateurs qui ne nécessitent pas de prétraitement.

Chargement AJAX

Vous pourriez charger un script supplémentaire avec un appel AJAX et ensuite utiliser eval pour l'exécuter. C'est la méthode la plus simple, mais elle est limitée à votre domaine à cause du modèle de sécurité de la sandbox JavaScript. L'utilisation de eval ouvre également la porte aux bogues, aux hacks et aux problèmes de sécurité.

Chargement des données

Comme pour les importations dynamiques, vous pouvez charger un ou plusieurs scripts avec un appel fetch en utilisant des promesses pour contrôler l'ordre d'exécution des dépendances du script en utilisant la bibliothèque Fetch Inject :

fetchInject([
  'https://cdn.jsdelivr.net/momentjs/2.17.1/moment.min.js'
]).then(() => {
  console.log(`Finish in less than ${moment().endOf('year').fromNow(true)}`)
})

Chargement de jQuery

La bibliothèque [jQuery][1] fournit une fonctionnalité de chargement [en une seule ligne][2] :

$.getScript("my_lovely_script.js", function() {
   alert("Script loaded but not necessarily executed.");
});

Chargement dynamique des scripts

Vous pouvez ajouter une balise script avec l'URL du script dans le HTML. Pour éviter la surcharge de jQuery, c'est une solution idéale.

Le script peut même résider sur un autre serveur. En outre, le navigateur évalue le code. La balise <script> peut être injectée soit dans la page web , soit insérée juste avant la balise de fermeture.

Voici un exemple de la façon dont cela peut fonctionner :

function dynamicallyLoadScript(url) {
    var script = document.createElement("script");  // create a script DOM node
    script.src = url;  // set its src to the provided URL

    document.head.appendChild(script);  // add it to the end of the head section of the page (could change 'head' to 'body' to add it to the end of the body section instead)
}

Cette fonction ajoutera une nouvelle balise <script> à la fin de la section head de la page, où l'attribut src est fixé à l'URL qui est donnée à la fonction comme premier paramètre.

Ces deux solutions sont présentées et illustrées dans [JavaScript Madness : Dynamic Script Loading][3].

Détecter quand le script a été exécuté

Maintenant, il y a un gros problème que vous devez connaître. Faire cela implique que vous chargez le code à distance. Les navigateurs web modernes chargeront le fichier et continueront à exécuter votre script actuel car ils chargent tout de manière asynchrone pour améliorer les performances. (Cela s'applique à la fois à la méthode jQuery et à la méthode manuelle de chargement dynamique du script).

Cela signifie que si vous utilisez ces astuces directement, vous ne pourrez pas utiliser votre code nouvellement chargé à la ligne suivante après avoir demandé son chargement, car il sera toujours en cours de chargement.

Par exemple : my_lovely_script.js contient MySuperObject :

var js = document.createElement("script");

js.type = "text/javascript";
js.src = jsFilePath;

document.body.appendChild(js);

var s = new MySuperObject();

Error : MySuperObject is undefined

Puis vous rechargez la page en tapant F5. Et ça marche ! Confusion...

Alors, qu'est ce qu'on peut y faire ?

Eh bien, vous pouvez utiliser le hack que l'auteur suggère dans le lien que je vous ai donné. En résumé, pour les personnes pressées, il utilise un événement pour exécuter une fonction de rappel lorsque le script est chargé. Ainsi, vous pouvez mettre tout le code utilisant la bibliothèque distante dans la fonction de rappel. Par exemple :

function loadScript(url, callback)
{
    // Adding the script tag to the head as suggested before
    var head = document.head;
    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = url;

    // Then bind the event to the callback function.
    // There are several events for cross browser compatibility.
    script.onreadystatechange = callback;
    script.onload = callback;

    // Fire the loading
    head.appendChild(script);
}

Ensuite, vous écrivez le code que vous voulez utiliser APRÈS le chargement du script dans une [fonction lambda][4] :

var myPrettyCode = function() {
   // Here, do whatever you want
};

Puis vous exécutez tout ça :

loadScript("my_lovely_script.js", myPrettyCode);

Notez que le script peut s'exécuter après le chargement du DOM, ou avant, selon le navigateur et si vous avez inclus la ligne script.async = false;. Il existe un [excellent article sur le chargement de Javascript en général] (http://www.html5rocks.com/en/tutorials/speed/script-loading/) qui traite de ce sujet.

Fusion du code source/prétraitement

Comme nous l'avons mentionné au début de cette réponse, de nombreux développeurs utilisent des outils de construction/transpilation tels que Parcel, Webpack ou Babel dans leurs projets, ce qui leur permet d'utiliser la nouvelle syntaxe JavaScript, d'assurer la rétrocompatibilité avec les anciens navigateurs, de combiner des fichiers, de réduire le code, de le fractionner, etc.

[1] : http://jquery.com/ [2] : http://api.jquery.com/jQuery.getScript/ [3] : http://unixpapa.com/js/dyna.html [4] : http://en.wikipedia.org/wiki/Anonymous_function

Commentaires (31)

Il est possible de générer dynamiquement une balise JavaScript et de l'ajouter au document HTML à partir d'un autre code JavaScript. Ceci chargera le fichier JavaScript ciblé.

function includeJs(jsFilePath) {
    var js = document.createElement("script");

    js.type = "text/javascript";
    js.src = jsFilePath;

    document.body.appendChild(js);
}

includeJs("/path/to/some/file.js");
Commentaires (13)

Vous pouvez peut-être utiliser cette fonction que j'ai trouvée sur cette page [Comment inclure un fichier JavaScript dans un fichier JavaScript ?][1] :

function include(filename)
{
    var head = document.getElementsByTagName('head')[0];

    var script = document.createElement('script');
    script.src = filename;
    script.type = 'text/javascript';

    head.appendChild(script)
}

[1] : http://forums.digitalpoint.com/showthread.php?t=146094

Commentaires (7)