Comment inclure un fichier JavaScript dans un autre fichier JavaScript?

5195

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

Alec Smart
la source
82
@Daniel, je ne veux pas utiliser un appel AJAX.
Alec Smart
13
Pourquoi ne pas déclarer le fichier importé avant celui qui en a besoin, simplement en utilisant des scriptbalises ordonnées ?
falsarella
6
@Claudiu Cela n'aiderait pas à importer quoi que ce soit, mais cela devrait aussi fonctionner. Si vous avez un fichier JS qui dépend d'un autre fichier JS, déclarez tout d'abord les balises de script des fichiers de dépendance, de sorte que le dernier aura déjà ses dépendances chargées. Si vous avez une situation où ce n'est pas une approche possible, les réponses ici devraient être utiles.
falsarella
1
quel est l'avantage pratique de faire cela? de toute façon la base de code dépendante du fichier javascript ne va pas se charger et commencer à fonctionner dans tous les cas elle n'est pas chargée!
Ciasto piekarz

Réponses:

4473

Les anciennes versions de JavaScript n'avaient pas d'importation, d'inclusion ou d'exigence, donc de nombreuses approches différentes de ce problème ont été développées.

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

Pour la compatibilité avec les anciens navigateurs, des outils de construction comme Webpack et Rollup et / ou outils de transpilation comme Babel peuvent être utilisés.

Modules ES6

Les modules ECMAScript (ES6) sont pris en charge dans Node.js depuis la v8.5, avec l' --experimental-modulesindicateur, et depuis au moins Node.js v13.8.0 sans l'indicateur. Pour activer « ESM » ( par rapport précédent système de module de style CommonJS de Node.js [ « CJS »]) vous soit utiliser "type": "module"dans package.jsonou donner les fichiers l'extension .mjs. (De même, les modules écrits avec le module CJS précédent de Node.js peuvent être nommés .cjssi votre valeur par défaut est ESM.)

En utilisant package.json:

{
    "type": "module"
}

Ensuite module.js:

export function hello() {
  return "Hello";
}

Ensuite main.js:

import { hello } from './module.js';
let val = hello();  // val is "Hello";

En utilisant .mjs, vous auriez module.mjs:

export function hello() {
  return "Hello";
}

Ensuite main.mjs:

import { hello } from './module.mjs';
let val = hello();  // val is "Hello";

Modules ECMAScript dans les navigateurs

Les navigateurs prennent en charge le chargement direct des modules ECMAScript (aucun outil tel que Webpack n'est requis) depuis Safari 10.1, Chrome 61, Firefox 60 et Edge 16. Vérifiez la prise en charge actuelle sur caniuse . Il n'est pas nécessaire d'utiliser l' .mjsextension Node.js ; les navigateurs ignorent complètement les extensions de fichiers sur les modules / scripts.

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

En savoir plus 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>

En savoir plus sur https://developers.google.com/web/updates/2017/11/dynamic-import

Node.js nécessite

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

// 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 façons pour JavaScript d'inclure du contenu JavaScript externe dans les navigateurs qui ne nécessitent pas de prétraitement.

Chargement AJAX

Vous pouvez charger un script supplémentaire avec un appel AJAX puis l'utiliser evalpour l'exécuter. C'est le moyen le plus simple, mais il est limité à votre domaine en raison du modèle de sécurité JavaScript sandbox. L'utilisation evalouvre également la porte à des bugs, des hacks et des problèmes de sécurité.

Récupérer le chargement

Comme Dynamic Imports, vous pouvez charger un ou plusieurs scripts avec un fetchappel en utilisant des promesses pour contrôler l'ordre d'exécution des dépendances de script à l'aide de 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 fournit une fonctionnalité de chargement sur une seule ligne :

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

Chargement de script dynamique

Vous pouvez ajouter une balise de script avec l'URL du script dans le code HTML. Pour éviter les frais généraux de jQuery, c'est une solution idéale.

Le script peut même résider sur un autre serveur. De plus, le navigateur évalue le code. La <script>balise peut être injectée dans la page Web <head>ou insérée juste avant la </body>balise de fermeture .

Voici un exemple de la façon dont cela pourrait 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 <script>balise à la fin de la section d'en-tête de la page, où l' srcattribut est défini sur l'URL qui est donnée à la fonction comme premier paramètre.

Ces deux solutions sont discutées et illustrées dans JavaScript Madness: Dynamic Script Loading .

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

Maintenant, il y a un gros problème que vous devez connaître. Cela implique que vous chargez le code à distance . Les navigateurs Web modernes chargeront le fichier et continueront d'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 de chargement manuel de script dynamique.)

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.jscontient 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

Ensuite, vous rechargez la page qui frappe F5. Et il fonctionne! Déroutant...

Que faire alors?

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é. Vous pouvez donc mettre tout le code à l'aide de 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 souhaitez utiliser APRÈS le chargement du script dans une fonction lambda :

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

Ensuite, vous exécutez tout cela:

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 y a un excellent article sur le chargement de Javascript en général qui en discute.

Fusion / prétraitement du code source

Comme mentionné en haut de cette réponse, de nombreux développeurs utilisent des outils de construction / transpilation comme Parcel, Webpack ou Babel dans leurs projets, leur permettant d'utiliser la syntaxe JavaScript à venir, de fournir une compatibilité descendante pour les navigateurs plus anciens, de combiner des fichiers, de réduire, effectuer le fractionnement de code, etc.

e-satis
la source
131
Non, mais quelqu'un qui utilise quelque chose d'aussi avancé que Rhino ou bien ne poserait pas cette question.
e-satis
193
Juste pour être complet, il y a une troisième façon: dans certaines solutions lorsque vous contrôlez les deux fichiers javascript, vous pouvez simplement créer 1 fichier javascript géant qui combine le contenu des deux fichiers.
Toad
20
Ne devrait pas "document.createElement (" my_lovely_script.js ");" dans l'exemple être "document.createElement (" script ")"?
Russell Silva
14
Comment eval ouvre-t-il la porte aux hacks si c'est votre code que vous exécutez?
Vince Panuccio
6
Votre réponse nécessite quelques modifications pour IE ( onreadystatechangeévénement et readyStatepropriété). De plus, le chargement de script dynamique ne bénéficie pas des scanners de préchargement du navigateur. Recommander cet article HTML5Rocks: html5rocks.com/en/tutorials/speed/script-loading
ruhong
571

Si quelqu'un recherche quelque chose de plus avancé, essayez RequireJS . Vous obtiendrez des avantages supplémentaires tels que la gestion des dépendances, une meilleure simultanéité et éviterez la duplication (c'est-à-dire la récupération d'un script plus d'une fois).

Vous pouvez écrire vos fichiers JavaScript dans des "modules", puis les référencer en tant que dépendances dans d'autres scripts. Ou vous pouvez utiliser RequireJS comme une simple solution "allez chercher ce script".

Exemple:

Définissez les dépendances en tant que modules:

some-dependency.js

define(['lib/dependency1', 'lib/dependency2'], function (d1, d2) {

     //Your actual script goes here.   
     //The dependent scripts will be fetched if necessary.

     return libraryObject;  //For example, jQuery object
});

implementation.js est votre fichier JavaScript "principal" qui dépend de some-dependency.js

require(['some-dependency'], function(dependency) {

    //Your script goes here
    //some-dependency.js is fetched.   
    //Then your script is executed
});

Extrait du README GitHub :

RequireJS charge des fichiers JavaScript simples ainsi que des modules plus définis. Il est optimisé pour une utilisation dans le navigateur, y compris dans un Web Worker, mais il peut être utilisé dans d'autres environnements JavaScript, comme Rhino et Node. Il implémente l'API du module asynchrone.

RequireJS utilise des balises de script simples pour charger les modules / fichiers, il devrait donc permettre un débogage facile. Il peut être utilisé simplement pour charger des fichiers JavaScript existants, vous pouvez donc l'ajouter à votre projet existant sans avoir à réécrire vos fichiers JavaScript.

...

John Strickler
la source
1
@aaaidan: La raison de MattDmo en plus, elle repose sur une bibliothèque externe qui, en retour, s'appuie sur la réponse acceptée.
David Mulder
1
Pour surmonter require.js, le plus récent serait le js angulaire qui est plus stable et facile à utiliser avec d'autres fonctions de liaison et HTML riches.
2014
5
-1: Ces abstractions - "some_dependency" - sont vraiment médiocres, les index ajoutant à la confusion. J'ai du mal à comprendre à quoi ressemble un exemple de code de travail. Si l'auteur fournissait un exemple de travail, presque n'importe qui pourrait l'adapter et le généraliser à ses besoins.
Tegiri Nenashi
1
ne fonctionne pas bien avec MVC et le regroupement de scripts avec minification automatique
Triynko
1
'ajouter .. sans avoir à réécrire vos fichiers JavaScript'; cool, mais comment exactement? Cabot. la réponse suggère d'ajouter un tel «besoin» (et non «requirejs»?) au script principal + un tel «définition» à chaque fichier dépendant, oui? Mais c'est réécrire, alors qu'est-ce qui n'a pas été réécrit? -rest de code puisque RequireJS permet à chaque fichier de faire des définitions globales comme d'habitude? -le fait? Je viens d'essayer cela, + (oublié?) <Script src = " requirejs.org/docs/release/2.2.0/comments/require.js "> </script>, puis 'requirejs ([' GoogleDrive.com/host / ..', ..
Destiny Architect
196

Il existe en fait un moyen de charger un fichier JavaScript de manière non asynchrone, vous pouvez donc utiliser les fonctions incluses dans votre fichier nouvellement chargé juste après son chargement, et je pense que cela fonctionne dans tous les navigateurs.

Vous devez utiliser jQuery.append()sur l' <head>élément de votre page, c'est-à-dire:

$("head").append('<script type="text/javascript" src="' + script + '"></script>');

Cependant, cette méthode a également un problème: si une erreur se produit dans le fichier JavaScript importé, Firebug (ainsi que Firefox Error Console et Chrome Developer Tools également) signaleront sa place de manière incorrecte, ce qui est un gros problème si vous utilisez Firebug pour suivre Beaucoup d'erreurs JavaScript (je le fais). Firebug ne connaît tout simplement pas le fichier nouvellement chargé pour une raison quelconque, donc si une erreur se produit dans ce fichier, il signale qu'elle s'est produite dans votre fichier HTML principal , et vous aurez du mal à trouver la vraie raison de l'erreur.

Mais si cela ne vous pose pas de problème, cette méthode devrait fonctionner.

J'ai en fait écrit un plugin jQuery appelé $ .import_js () qui utilise cette méthode:

(function($)
{
    /*
     * $.import_js() helper (for JavaScript importing within JavaScript code).
     */
    var import_js_imported = [];

    $.extend(true,
    {
        import_js : function(script)
        {
            var found = false;
            for (var i = 0; i < import_js_imported.length; i++)
                if (import_js_imported[i] == script) {
                    found = true;
                    break;
                }

            if (found == false) {
                $("head").append('<script type="text/javascript" src="' + script + '"></script>');
                import_js_imported.push(script);
            }
        }
    });

})(jQuery);

Donc, tout ce que vous devez faire pour importer JavaScript est:

$.import_js('/path_to_project/scripts/somefunctions.js');

J'ai également fait un test simple pour cela à Example .

Il inclut un main.jsfichier dans le HTML principal puis le script main.jsutilisé $.import_js()pour importer un fichier supplémentaire appelé included.js, qui définit cette fonction:

function hello()
{
    alert("Hello world!");
}

Et juste après l'inclusion included.js, la hello()fonction est appelée et vous obtenez l'alerte.

(Cette réponse fait suite au commentaire d'e-satis).

Kipras
la source
J'essaie cette méthode, mais ne fonctionne pas pour moi, l'élément n'apparaît tout simplement pas dans la balise head.
sites
16
@juanpastas - utilisez jQuery.getScript, de cette façon vous n'avez pas à vous soucier de l'écriture du plugin ...
MattDMo
6
Hmm, selon cet article , ajouter un scriptélément à le headfera fonctionner de manière asynchrone, sauf si le asyncest spécifiquement défini sur false.
Flimm
1
La variable de script ne devrait-elle pas avoir des entités html encodées? Si le lien contient le ", le code se cassera
RedClover
1
@Flimm cher monsieur, mon équipe et moi-même remercions pour votre commentaire et je vous dois personnellement une bière d'argent égal si nous nous rencontrons jamais en personne
Alex
155

Une autre façon, qui à mon avis est beaucoup plus propre, est de faire une requête Ajax synchrone au lieu d'utiliser une <script>balise. C'est également ainsi que Node.js gère les inclus.

Voici un exemple utilisant jQuery:

function require(script) {
    $.ajax({
        url: script,
        dataType: "script",
        async: false,           // <-- This is the key
        success: function () {
            // all good...
        },
        error: function () {
            throw new Error("Could not load script " + script);
        }
    });
}

Vous pouvez ensuite l'utiliser dans votre code comme vous utiliseriez habituellement un include:

require("/scripts/subscript.js");

Et pouvoir appeler une fonction à partir du script requis sur la ligne suivante:

subscript.doSomethingCool(); 
Ariel
la source
1
Bonne solution, la tête inclut est asynchrone malheureusement, la solution ajax fonctionne.
Matteo Conta
17
Comme quelqu'un l'a mentionné, requirejs.org le fait et dispose également d'un précompilateur qui rassemble les fichiers js afin qu'ils se chargent plus rapidement. Tu voudras peut-être vérifier.
Ariel
2
J'ai trouvé que je pouvais le déboguer en ajoutant cette directive au bas du fichier pour Chrome: // @ sourceURL = view_index.js
Todd Vance
11
malheureusement, async: false est désormais déconseillé dans jQuery. Pourrait se casser à l'avenir, donc j'éviterais.
sqram
3
@katsh Nous n'utilisons pas d'objets jqXHR ici. Votre citation ne semble pas étayer votre commentaire précédent indiquant qu'il async: falseest censé être obsolète. Ce n'est pas! Comme l'indique votre citation, seuls les éléments liés à jqXHR le sont.
Zero3
101

Il y a une bonne nouvelle pour toi. Très bientôt, vous pourrez facilement charger du code JavaScript. Il deviendra un moyen standard d'importer des modules de code JavaScript et fera partie du noyau JavaScript lui-même.

Il suffit d'écrire import cond from 'cond.js';pour charger une macro nommée à condpartir d'un fichier cond.js.

Vous n'avez donc pas à vous fier à un framework JavaScript ni à faire explicitement des appels Ajax .

Faire référence à:

Imdad
la source
12
require / import sur le jsfile a été beaucoup trop long à venir. (OMI).
rwheadon
6
@rwheadon ouais semble épouvantable que cela ne fasse pas partie de la langue! Comment les gens peuvent-ils faire quelque chose me dépasse! Je suis nouveau dans ce domaine et cela semble le pire (de nombreux) morceaux de folie
JonnyRaa
@ jonny-leeds Même sans chargement de module intégré, JavaScript dans le navigateur est suffisamment flexible pour que nous puissions implémenter une bibliothèque comme RequireJS pour notre gestion de module.
Keen
8
mi 2015 - Toujours pas implémenté dans les navigateurs, developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
scape
Oui tu as raison. maintenant je commence à me sentir mal pour ça :(. Thouh, une fois implémenté dans tous les navigateurs, ce sera une excellente fonctionnalité intégrée à javascript.
Imdad
97

Il est possible de générer dynamiquement une balise JavaScript et de l'ajouter au document HTML à partir d'un autre code JavaScript. Cela 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");
Svitlana Maksymchuk
la source
8
@ e-satis - En fait, c'est un avantage, un script de synchronisation serait bloquant. Chevaux pour les cours, mais 9 fois sur 10 vous voulez l'option non bloquante.
annakata
@Svitlana - les éléments de script créés comme celui-ci sont asynchrones. Actuellement, cela pourrait être considéré comme exploitant une échappatoire, ce qui pourrait ne pas être à l'épreuve du temps, je n'ai rien vu dans une norme qui clarifie cela.
annakata
En fait, je le reprends, j'ai remarqué que vous ajoutez au corps plutôt qu'à la tête.
annakata
@annakata: Quelle est la différence? BTW, yahoo suggère d'ajouter des balises de script à la fin du corps, donc rien ne devrait lui poser problème lors de l'ajout dynamique.
Svitlana Maksymchuk
7
@ e-satis asynchronous est bon car il ne gèle pas votre page. Utilisez le rappel pour être averti quand c'est faitjs.onload = callback;
Vitim.us
74

La déclaration importest dans ECMAScript 6.

Syntaxe

import name from "module-name";
import { member } from "module-name";
import { member as alias } from "module-name";
import { member1 , member2 } from "module-name";
import { member1 , member2 as alias2 , [...] } from "module-name";
import name , { member [ , [...] ] } from "module-name";
import "module-name" as name;
draupnie
la source
... mais n'est pris en charge par aucun navigateur à ce jour, selon le tableau de compatibilité sur la page à laquelle vous êtes lié.
Zero3
6
Vous pouvez maintenant écrire du code ES6 et le compiler avec Babel.js ( babeljs.io ) dans le système de modules actuel préféré (CommonJS / AMD / UMD): babeljs.io/docs/usage/modules
Jeremy Harris
@ Zero3 Apparemment, le nouvel IE (Edge) est le seul
Julian Avar
en 2019, IE ne prend toujours pas cela en charge sous aucune forme. Pourquoi Microsoft doit-il être si contraire à la convivialité?
GreySage
61

Peut-être pouvez-vous utiliser cette fonction que j'ai trouvée sur cette page Comment puis-je inclure un fichier JavaScript dans un fichier JavaScript? :

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

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

    head.appendChild(script)
}
Arnaud Gouder de Beauregard
la source
5
Devrait être utile d'ajouterscript.onload = callback;
Vitim.us
@SvitlanaMaksymchuk donc, si je n'utilise pas var, la variable sera globale?
Francisco Corrales Morales
@FranciscoCorrales oui.
Christopher Chiche
On finit en global avec ou sans le var :)
Vedran Maricevic.
Cela échoue si la page n'a pashead .
Dan Dascalescu
54

Voici une version synchrone sans jQuery :

function myRequire( url ) {
    var ajax = new XMLHttpRequest();
    ajax.open( 'GET', url, false ); // <-- the 'false' makes it synchronous
    ajax.onreadystatechange = function () {
        var script = ajax.response || ajax.responseText;
        if (ajax.readyState === 4) {
            switch( ajax.status) {
                case 200:
                    eval.apply( window, [script] );
                    console.log("script loaded: ", url);
                    break;
                default:
                    console.log("ERROR: script not loaded: ", url);
            }
        }
    };
    ajax.send(null);
}

Notez que pour obtenir cet inter-domaine fonctionnel, le serveur devra définir l'en- allow-origintête dans sa réponse.

heinob
la source
Excellente fonction! Charge le JavaScript avant que tout JS supplémentaire ne soit écrit après le corps. Très important lors du chargement de plusieurs scripts.
tfont
3
@heinob: Que puis-je faire pour le faire fonctionner pour le domaine croisé? (chargement du script depuis http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStuff/userjs/aagmfunctions.js)
user2284570
@ user2284570: Si vous êtes le propriétaire du domaine étranger: définissez l'en-tête "allow-origin" dans la réponse du serveur. Si vous n'êtes pas propriétaire: rien. Désolé! C'est la politique de l'origine croisée.
heinob
2
@ user2284570: Je comprends votre commentaire de cette façon, que vous n'êtes pas le propriétaire du domaine à partir duquel vous souhaitez charger le script. Dans ce cas, vous pouvez uniquement charger un script via une <script>balise insérée , pas via XMLHttpRequest.
heinob
2
Pour ceux qui envisagent de l'utiliser dans Firefox (par exemple pour les scripts imacros), ajoutez cette ligne en haut du fichier:const XMLHttpRequest = Components.Constructor("@mozilla.org/xmlextras/xmlhttprequest;1");
Kwestion
49

Je viens d'écrire ce code JavaScript (en utilisant Prototype pour la manipulation DOM ):

var require = (function() {
    var _required = {};
    return (function(url, callback) {
        if (typeof url == 'object') {
            // We've (hopefully) got an array: time to chain!
            if (url.length > 1) {
                // Load the nth file as soon as everything up to the
                // n-1th one is done.
                require(url.slice(0, url.length - 1), function() {
                    require(url[url.length - 1], callback);
                });
            } else if (url.length == 1) {
                require(url[0], callback);
            }
            return;
        }
        if (typeof _required[url] == 'undefined') {
            // Haven't loaded this URL yet; gogogo!
            _required[url] = [];

            var script = new Element('script', {
                src: url,
                type: 'text/javascript'
            });
            script.observe('load', function() {
                console.log("script " + url + " loaded.");
                _required[url].each(function(cb) {
                    cb.call(); // TODO: does this execute in the right context?
                });
                _required[url] = true;
            });

            $$('head')[0].insert(script);
        } else if (typeof _required[url] == 'boolean') {
            // We already loaded the thing, so go ahead.
            if (callback) {
                callback.call();
            }
            return;
        }

        if (callback) {
            _required[url].push(callback);
        }
    });
})();

Usage:

<script src="prototype.js"></script>
<script src="require.js"></script>
<script>
    require(['foo.js','bar.js'], function () {
        /* Use foo.js and bar.js here */
    });
</script>

Gist: http://gist.github.com/284442 .

nornagon
la source
7
jrburke a écrit ceci comme RequireJS. Github: requirejs.org/docs/requirements.html
Mike Caron
N'est-ce pas mettre le script chargé en dehors de la portée où require () est appelé? On dirait que eval () est le seul moyen de le faire dans le cadre. Ou existe-t-il un autre moyen?
trusktr
44

Voici la version généralisée de la façon dont Facebook le fait pour leur bouton J'aime omniprésent:

<script>
  var firstScript = document.getElementsByTagName('script')[0],
      js = document.createElement('script');
  js.src = 'https://cdnjs.cloudflare.com/ajax/libs/Snowstorm/20131208/snowstorm-min.js';
  js.onload = function () {
    // do stuff with your dynamically loaded script
    snowStorm.snowColor = '#99ccff';
  };
  firstScript.parentNode.insertBefore(js, firstScript);
</script>

Si cela fonctionne pour Facebook, cela fonctionnera pour vous.

La raison pour laquelle nous recherchons le premier scriptélément au lieu de headou bodyest parce que certains navigateurs n'en créent pas s'il est manquant, mais nous avons la garantie d'avoir un scriptélément - celui-ci. En savoir plus sur http://www.jspatterns.com/the-ridiculous-case-of-adding-a-script-element/ .

Dan Dascalescu
la source
3
Sacrément belle! Certaines des méthodes fonctionnent ici aussi, mais dans un cadre dynamique, cela fonctionne le mieux.
tfont
j'ai étendu votre script pour éliminer les doublons
Kamil Dąbrowski
39

Si vous voulez en JavaScript pur, vous pouvez utiliser document.write.

document.write('<script src="myscript.js" type="text/javascript"></script>');

Si vous utilisez la bibliothèque jQuery, vous pouvez utiliser la $.getScriptméthode.

$.getScript("another_script.js");
Venu immadi
la source
8
document.write ne supprimerait-il pas tout le reste?
Eisa Adil
30

Vous pouvez également assembler vos scripts en utilisant PHP :

Fichier main.js.php:

<?php
    header('Content-type:text/javascript; charset=utf-8');
    include_once("foo.js.php");
    include_once("bar.js.php");
?>

// Main JavaScript code goes here
Calmarius
la source
16
On dirait que le but est de garder tout cela en javascript dans le frontal
Ariel
1
Merci de l'avoir rappelé. Vous pouvez également avoir des balises PHP d'écriture <script> dans votre en-tête HTML, afin que les fichiers js dont vous avez besoin (et seulement ceux-ci) soient chargés.
Rolf
29

La plupart des solutions présentées ici impliquent un chargement dynamique. Je cherchais plutôt un compilateur qui assemble tous les fichiers dépendants en un seul fichier de sortie. Les mêmes que les préprocesseurs Less / Sass traitent la @importrègle CSS at. Comme je n'ai rien trouvé de convenable de ce genre, j'ai écrit un outil simple pour résoudre le problème.

Voici donc le compilateur, https://github.com/dsheiko/jsic , qui remplace $import("file-path")en toute sécurité le contenu du fichier demandé. Voici le plugin Grunt correspondant : https://github.com/dsheiko/grunt-jsic .

Sur la branche principale jQuery, ils concaténent simplement les fichiers source atomiques en un seul commençant par intro.jset se terminant par outtro.js. Cela ne me convient pas car il n'offre aucune flexibilité sur la conception du code source. Découvrez comment cela fonctionne avec jsic:

src / main.js

var foo = $import("./Form/Input/Tel");

src / Form / Input / Tel.js

function() {
    return {
          prop: "",
          method: function(){}
    }
}

Maintenant, nous pouvons exécuter le compilateur:

node jsic.js src/main.js build/mail.js

Et obtenez le fichier combiné

build / main.js

var foo = function() {
    return {
          prop: "",
          method: function(){}
    }
};
Dmitry Sheiko
la source
2
Depuis cette publication, j'ai trouvé une bien meilleure solution - compilateur de modules CommonJS - github.com/dsheiko/cjsc Vous pouvez donc simplement écrire des modules CommonJs ou NodeJs et accéder les uns aux autres tout en les conservant dans des étendues isolées. Les avantages: pas besoin de plusieurs requêtes HTTP qui affectent les performances Vous n'avez pas besoin d'envelopper manuellement le code de votre module - c'est la responsabilité du compilateur (donc meilleure lisibilité du code source) Vous n'avez pas besoin de bibliothèques externes Il est compatible avec UMD- et modules NodeJs (par exemple, vous pouvez adresser jQuery, Backbone en tant que modules sans toucher à leur code)
Dmitry Sheiko
26

Si votre intention de charger le fichier JavaScript utilise les fonctions du fichier importé / inclus , vous pouvez également définir un objet global et définir les fonctions comme éléments d'objet. Par exemple:

global.js

A = {};

file1.js

A.func1 = function() {
  console.log("func1");
}

file2.js

A.func2 = function() {
  console.log("func2");
}

main.js

A.func1();
A.func2();

Vous devez juste être prudent lorsque vous incluez des scripts dans un fichier HTML. L'ordre doit être comme ci-dessous:

<head>
  <script type="text/javascript" src="global.js"></script>
  <script type="text/javascript" src="file1.js"></script>
  <script type="text/javascript" src="file2.js"></script>
  <script type="text/javascript" src="main.js"></script>
</head>
Adem İlhan
la source
22

Cela devrait faire:

xhr = new XMLHttpRequest();
xhr.open("GET", "/soap/ajax/11.0/connection.js", false);
xhr.send();
eval(xhr.responseText);
tggagne
la source
5
l' evalest ce qui ne va pas avec elle. De Crockford , " evalest mauvais. La evalfonction est la fonctionnalité la plus mal utilisée de JavaScript. Évitez-la. A evaldes alias. N'utilisez pas le Functionconstructeur. Ne passez pas de chaînes à setTimeoutou setInterval." Si vous n'avez pas lu son "JavaScript: The Good Parts" alors sortez et faites-le maintenant. Vous allez ne le regretterez pas.
MattDMo
13
@MattDMo "Quelqu'un a dit que c'était mauvais" n'est pas vraiment un argument.
Casey
4
@emodendroket Je suppose que vous ne savez pas qui est Douglas Crockford .
MattDMo
13
@MattDMo Je suis parfaitement conscient de qui il est, mais c'est un être humain, pas un dieu.
Casey
2
@tggagne: Que puis-je faire pour le faire fonctionner pour le domaine croisé? (chargement du script depuis http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStuff/userjs/aagmfunctions.js)
user2284570
21

Ou plutôt que d'inclure au moment de l'exécution, utilisez un script pour concaténer avant le téléchargement.

J'utilise des pignons (je ne sais pas s'il y en a d'autres). Vous créez votre code JavaScript dans des fichiers séparés et incluez les commentaires qui sont traités par le moteur Sprockets comme inclus. Pour le développement, vous pouvez inclure des fichiers séquentiellement, puis pour la production pour les fusionner ...

Voir également:

JMawer
la source
Browserify est beaucoup plus populaire que Sprockets.
Dan Dascalescu
18

J'ai eu un problème simple, mais j'ai été déconcerté par les réponses à cette question.

J'ai dû utiliser une variable (myVar1) définie dans un fichier JavaScript (myvariables.js) dans un autre fichier JavaScript (main.js).

Pour cela, j'ai fait comme ci-dessous:

Chargé le code JavaScript dans le fichier HTML, dans l'ordre correct, myvariables.js d'abord, puis main.js:

<html>
    <body onload="bodyReady();" >

        <script src="myvariables.js" > </script>
        <script src="main.js" > </script>

        <!-- Some other code -->
    </body>
</html>

Fichier: myvariables.js

var myVar1 = "I am variable from myvariables.js";

Fichier: main.js

// ...
function bodyReady() {
    // ...
    alert (myVar1);    // This shows "I am variable from myvariables.js", which I needed
    // ...
}
// ...

Comme vous l'avez vu, j'ai dû utiliser une variable dans un fichier JavaScript dans un autre fichier JavaScript, mais je n'avais pas besoin d'en inclure une dans un autre. J'avais juste besoin de m'assurer que le premier fichier JavaScript était chargé avant le deuxième fichier JavaScript et que les variables du premier fichier JavaScript sont accessibles dans le deuxième fichier JavaScript, automatiquement.

Cela m'a sauvé la journée. J'espère que ça aide.

Manohar Reddy Poreddy
la source
Le problème avec cette réponse est que ce n'est pas quelque chose comme ça import. Vous avez besoin d'un fichier HTML pour obtenir des informations d'un fichier js à un autre.
Cannicide le
Très vrai. C'est ce que d'autres réponses proposent comme solutions. Cependant, j'avais deux fichiers JS, l'un devrait utiliser les autres variables de JS. Aucun node.js, next.js, express.js, etc., donc importer OU exiger ne fonctionnera pas, juste des fichiers .js simples.
Manohar Reddy Poreddy
Compréhensible, cependant, la question demande en fait d'importer un fichier javascript dans un autre fichier lui-même, et pas seulement d'accéder à ses variables à l'aide d'un fichier HTML. Par exemple, supposons que vous créez une fonctionnalité pour un bouton et que vous utilisez trois fichiers js différents, un pour la gestion des clics, un pour la gestion du survol, un pour les rappels pour chacun des deux autres. Il serait utile d'importer les deux autres js dans le troisième js lui-même et de n'avoir qu'une seule <script>balise. Cela peut aider à l'organisation. Cette réponse n'est tout simplement pas ce que la question demandait et n'est pas idéale dans ce contexte.
Cannicide
Lorsque j'ai cherché une solution à mon problème, j'ai cherché dans le bon sens, mais Google pense que c'est la bonne page, j'ai donc atterri ici. C'est pourquoi j'ai écrit ma réponse ici. On dirait que j'ai bien fait, en raison de 15 votes positifs ci-dessus. Pour ne pas perdre le temps des autres, j'ai mis un début clair sur ce qu'est mon scénario - j'ai mis le code HTML en premier - qui semble aidé, puisque vous avez répondu et dit que le HTML n'est pas votre scénario. J'espère que cela a clarifié.
Manohar Reddy Poreddy
Oui - en effet, il semble que certaines personnes ont recherché un problème légèrement différent, auquel il s'agit d'une solution, et l'ont trouvé. Bien que ce ne soit pas nécessairement la réponse à cette question, c'est certainement la réponse à une autre question similaire. En tant que tel, il est correct d'avoir cette réponse ici car cela aide ceux qui recherchaient cette réponse. Merci pour cette réponse, cela l'a clarifié.
Cannicide
16

Dans le cas où vous utilisez Web Workers et souhaitez inclure des scripts supplémentaires dans la portée du travailleur, les autres réponses fournies sur l'ajout de scripts à la headbalise, etc. ne fonctionneront pas pour vous.

Heureusement, les Web Workers ont leur propre importScriptsfonction qui est une fonction globale dans le cadre du Web Worker, native du navigateur lui-même car elle fait partie de la spécification .

Alternativement, en tant que deuxième réponse la plus votée à vos questions , RequireJS peut également gérer l'inclusion de scripts dans un Web Worker (probablement en s'appelant importScriptslui-même, mais avec quelques autres fonctionnalités utiles).

Turnerj
la source
16

En langage moderne avec la vérification si le script a déjà été chargé ce serait:

function loadJs(url){
  return new Promise( (resolve, reject) => {
    if (document.querySelector(`head > script[src="${src}"]`) !== null) return resolve()
    const script = document.createElement("script")
    script.src = url
    script.onload = resolve
    script.onerror = reject
    document.head.appendChild(script)
  });
}

Utilisation (asynchrone / attente):

try { await loadJs("https://.../script.js") } 
catch(error) {console.log(error)}

ou

await loadJs("https://.../script.js").catch(err => {})

Utilisation (promesse):

loadJs("https://.../script.js").then(res => {}).catch(err => {})
Dmitry Sheiko
la source
Agréable. Bonne solution.
Naftali aka Neal
Concis et n'a besoin que d'ES5, et évite avec élégance un rappel formel. Merci Dmitry!
Eureka
Wow, fonctionne dans le navigateur sans serveur. pi.js = simplement var pi = 3.14. appeler la fonction loadJS () vialoadJs("pi.js").then(function(){ console.log(pi); });
zipzit
14

La @importsyntaxe pour réaliser l'importation JavaScript de type CSS est possible en utilisant un outil tel que Mixture via leur .mixtype de fichier spécial (voir ici ). J'imagine que l'application utilise simplement l'une des méthodes susmentionnées en interne, bien que je ne sache pas.

De la documentation Mixture sur les .mixfichiers:

Les fichiers de mixage sont simplement des fichiers .js ou .css avec .mix. dans le nom du fichier. Un fichier de mixage étend simplement les fonctionnalités d'un fichier de style ou de script normal et vous permet d'importer et de combiner.

Voici un exemple de .mixfichier qui combine plusieurs .jsfichiers en un seul:

// scripts-global.mix.js
// Plugins - Global

@import "global-plugins/headroom.js";
@import "global-plugins/retina-1.1.0.js";
@import "global-plugins/isotope.js";
@import "global-plugins/jquery.fitvids.js";

Le mélange produit cela en tant scripts-global.jsque version réduite (scripts-global.min.js ).

Remarque: je ne suis en aucun cas affilié à Mixture, à part l'utiliser comme outil de développement frontal. Je suis tombé sur cette question en voyant un .mixfichier JavaScript en action (dans l'un des passe-partout Mixture) et en étant un peu confus ("vous pouvez le faire?", Je me suis dit). Ensuite, j'ai réalisé qu'il s'agissait d'un type de fichier spécifique à l'application (quelque peu décevant, d'accord). Néanmoins, je pensais que les connaissances pourraient être utiles pour les autres.

MISE À JOUR : Le mélange est maintenant gratuit (hors ligne).

MISE À JOUR : Le mélange est maintenant interrompu. Les anciennes versions de mélange sont toujours disponibles

Isaac Gregson
la source
Ce serait génial s'il s'agissait d'un module de nœud.
b01
@ b01 Cela ressemble à un défi;) Si seulement j'avais le temps ... peut-être que quelqu'un d'autre le fait?
Isaac Gregson
13
var js = document.createElement("script");

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

document.body.appendChild(js);
Sam4Code
la source
2
Il s'agit du code le plus simple, mais il échouera dans certains cas extrêmes lorsqu'il bodyn'existe pas encore ou ne peut pas être modifié. En outre, cela aide à expliquer les réponses.
Dan Dascalescu
13

Ma méthode habituelle est:

var require = function (src, cb) {
    cb = cb || function () {};

    var newScriptTag = document.createElement('script'),
        firstScriptTag = document.getElementsByTagName('script')[0];
    newScriptTag.src = src;
    newScriptTag.async = true;
    newScriptTag.onload = newScriptTag.onreadystatechange = function () {
        (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') && (cb());
    };
    firstScriptTag.parentNode.insertBefore(newScriptTag, firstScriptTag);
}

Cela fonctionne très bien et n'utilise aucun rechargement de page pour moi. J'ai essayé la méthode AJAX (l'une des autres réponses) mais cela ne semble pas fonctionner aussi bien pour moi.

Voici une explication du fonctionnement du code pour ceux qui sont curieux: il crée essentiellement une nouvelle balise de script (après la première) de l'URL. Il le définit en mode asynchrone afin de ne pas bloquer le reste du code, mais appelle un rappel lorsque le readyState (l'état du contenu à charger) passe à «chargé».

Christopher Dumas
la source
13

Bien que ces réponses soient excellentes, il existe une "solution" simple qui existe depuis le chargement du script, et elle couvrira 99,999% des cas d'utilisation de la plupart des gens. Incluez simplement le script dont vous avez besoin avant le script qui l'exige. Pour la plupart des projets, il ne faut pas longtemps pour déterminer quels scripts sont nécessaires et dans quel ordre.

<!DOCTYPE HTML>
<html>
    <head>
        <script src="script1.js"></script>
        <script src="script2.js"></script>
    </head>
    <body></body>
</html>

Si script2 nécessite script1, c'est vraiment le moyen le plus simple de faire quelque chose comme ça. Je suis très surpris que personne n'ait soulevé cette question, car c'est la réponse la plus évidente et la plus simple qui s'appliquera dans presque tous les cas.

KthProg
la source
1
Je suppose que pour beaucoup de gens, dont moi-même, nous avons besoin d'un nom de fichier dynamique.
Stuart McIntyre
1
@StuartMcIntyre dans ce cas, définissez l'attribut src de la balise de script au moment de l'exécution et attendez l'événement onload (bien sûr, il existe de meilleures solutions dans ce cas en 2019).
KthProg
12

J'ai écrit un module simple qui automatise le travail d'importation / inclusion de scripts de module en JavaScript. Pour une explication détaillée du code, reportez-vous à l'article de blog JavaScript require / import / include modules .

// ----- USAGE -----

require('ivar.util.string');
require('ivar.net.*');
require('ivar/util/array.js');
require('http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js');

ready(function(){
    //Do something when required scripts are loaded
});

    //--------------------

var _rmod = _rmod || {}; //Require module namespace
_rmod.LOADED = false;
_rmod.on_ready_fn_stack = [];
_rmod.libpath = '';
_rmod.imported = {};
_rmod.loading = {
    scripts: {},
    length: 0
};

_rmod.findScriptPath = function(script_name) {
    var script_elems = document.getElementsByTagName('script');
    for (var i = 0; i < script_elems.length; i++) {
        if (script_elems[i].src.endsWith(script_name)) {
            var href = window.location.href;
            href = href.substring(0, href.lastIndexOf('/'));
            var url = script_elems[i].src.substring(0, script_elems[i].length - script_name.length);
            return url.substring(href.length+1, url.length);
        }
    }
    return '';
};

_rmod.libpath = _rmod.findScriptPath('script.js'); //Path of your main script used to mark
                                                   //the root directory of your library, any library.


_rmod.injectScript = function(script_name, uri, callback, prepare) {

    if(!prepare)
        prepare(script_name, uri);

    var script_elem = document.createElement('script');
    script_elem.type = 'text/javascript';
    script_elem.title = script_name;
    script_elem.src = uri;
    script_elem.async = true;
    script_elem.defer = false;

    if(!callback)
        script_elem.onload = function() {
            callback(script_name, uri);
        };
    document.getElementsByTagName('head')[0].appendChild(script_elem);
};

_rmod.requirePrepare = function(script_name, uri) {
    _rmod.loading.scripts[script_name] = uri;
    _rmod.loading.length++;
};

_rmod.requireCallback = function(script_name, uri) {
    _rmod.loading.length--;
    delete _rmod.loading.scripts[script_name];
    _rmod.imported[script_name] = uri;

    if(_rmod.loading.length == 0)
        _rmod.onReady();
};

_rmod.onReady = function() {
    if (!_rmod.LOADED) {
        for (var i = 0; i < _rmod.on_ready_fn_stack.length; i++){
            _rmod.on_ready_fn_stack[i]();
        });
        _rmod.LOADED = true;
    }
};

_.rmod = namespaceToUri = function(script_name, url) {
    var np = script_name.split('.');
    if (np.getLast() === '*') {
        np.pop();
        np.push('_all');
    }

    if(!url)
        url = '';

    script_name = np.join('.');
    return  url + np.join('/')+'.js';
};

//You can rename based on your liking. I chose require, but it
//can be called include or anything else that is easy for you
//to remember or write, except "import", because it is reserved
//for future use.
var require = function(script_name) {
    var uri = '';
    if (script_name.indexOf('/') > -1) {
        uri = script_name;
        var lastSlash = uri.lastIndexOf('/');
        script_name = uri.substring(lastSlash+1, uri.length);
    } 
    else {
        uri = _rmod.namespaceToUri(script_name, ivar._private.libpath);
    }

    if (!_rmod.loading.scripts.hasOwnProperty(script_name)
     && !_rmod.imported.hasOwnProperty(script_name)) {
        _rmod.injectScript(script_name, uri,
            _rmod.requireCallback,
                _rmod.requirePrepare);
    }
};

var ready = function(fn) {
    _rmod.on_ready_fn_stack.push(fn);
};
stamat
la source
10

Ce script ajoutera un fichier JavaScript en haut de toute autre <script>balise:

(function () {
    var li = document.createElement('script'); 
    li.type = 'text/javascript'; 
    li.src= "http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"; 
    li.async=true; 
    var s = document.getElementsByTagName('script')[0]; 
    s.parentNode.insertBefore(li, s);
})();
Vicky Gonsalves
la source
10

Il y a aussi Head.js . Il est très facile de gérer:

head.load("js/jquery.min.js",
          "js/jquery.someplugin.js",
          "js/jquery.someplugin.css", function() {
  alert("Everything is ok!");
});

Comme vous le voyez, c'est plus facile que Require.js et aussi pratique que la $.getScriptméthode de jQuery . Il possède également des fonctionnalités avancées, comme le chargement conditionnel, la détection de fonctionnalités et bien plus encore .

Ale
la source
10

Il existe de nombreuses réponses possibles à cette question. Ma réponse est évidemment basée sur un certain nombre d'entre eux. C'est ce que je me suis retrouvé après avoir lu toutes les réponses.

Le problème avec $.getScript et vraiment toute autre solution qui nécessite un rappel lorsque le chargement est terminé est que si vous avez plusieurs fichiers qui l'utilisent et dépendent les uns des autres, vous n'avez plus de moyen de savoir quand tous les scripts ont été chargés (une fois qu'ils sont imbriqués) dans plusieurs fichiers).

Exemple:

file3.js

var f3obj = "file3";

// Define other stuff

file2.js:

var f2obj = "file2";
$.getScript("file3.js", function(){

    alert(f3obj);

    // Use anything defined in file3.
});

file1.js:

$.getScript("file2.js", function(){
    alert(f3obj); //This will probably fail because file3 is only guaranteed to have loaded inside the callback in file2.
    alert(f2obj);

    // Use anything defined in the loaded script...
});

Vous avez raison lorsque vous dites que vous pouvez spécifier Ajax pour qu'il s'exécute de manière synchrone ou utiliser XMLHttpRequest , mais la tendance actuelle semble être de déprécier les demandes synchrones, de sorte que vous ne pouvez pas obtenir une prise en charge complète du navigateur maintenant ou à l'avenir.

Vous pouvez essayer d'utiliser $.whenpour vérifier un tableau d'objets différés, mais maintenant vous le faites dans chaque fichier et le fichier2 sera considéré comme chargé dès que le $.whensera exécuté et non lorsque le rappel sera exécuté, donc le fichier1 continue son exécution avant le chargement du fichier3 . Cela a vraiment toujours le même problème.

J'ai décidé de reculer au lieu d'avancer. Je vous remercie document.writeln. Je sais que c'est tabou, mais tant qu'il est utilisé correctement, cela fonctionne bien. Vous vous retrouvez avec du code qui peut être débogué facilement, s'affiche correctement dans le DOM et peut garantir l'ordre de chargement correct des dépendances.

Vous pouvez bien sûr utiliser $ ("body"). Append (), mais vous ne pourrez plus déboguer correctement.

REMARQUE: vous ne devez l'utiliser que pendant le chargement de la page, sinon vous obtenez un écran vide. En d'autres termes, placez toujours ceci avant / en dehors de document.ready . Je n'ai pas testé l'utilisation de cela après le chargement de la page dans un événement de clic ou quelque chose comme ça, mais je suis sûr que cela échouera.

J'ai aimé l'idée d'étendre jQuery, mais évidemment vous n'en avez pas besoin.

Avant d'appeler document.writeln, il vérifie que le script n'a pas déjà été chargé en évaluant tous les éléments du script.

Je suppose qu'un script n'est pas entièrement exécuté tant que son document.readyévénement n'a pas été exécuté. (Je sais que l'utilisation document.readyn'est pas obligatoire, mais beaucoup de gens l'utilisent, et la manipulation est une sauvegarde.)

Lorsque les fichiers supplémentaires sont chargés, les document.readyrappels seront exécutés dans le mauvais ordre. Pour résoudre ce problème lorsqu'un script est réellement chargé, le script qui l'a importé est lui-même réimporté et l'exécution arrêtée. Cela provoque le document.readyrappel du fichier d'origine exécuté à partir de n'importe quel script qu'il importe.

Au lieu de cette approche, vous pouvez essayer de modifier le jQuery readyList, mais cela semble être une pire solution.

Solution:

$.extend(true,
{
    import_js : function(scriptpath, reAddLast)
    {
        if (typeof reAddLast === "undefined" || reAddLast === null)
        {
            reAddLast = true; // Default this value to true. It is not used by the end user, only to facilitate recursion correctly.
        }

        var found = false;
        if (reAddLast == true) // If we are re-adding the originating script we do not care if it has already been added.
        {
            found = $('script').filter(function () {
                return ($(this).attr('src') == scriptpath);
            }).length != 0; // jQuery to check if the script already exists. (replace it with straight JavaScript if you don't like jQuery.
        }

        if (found == false) {

            var callingScriptPath = $('script').last().attr("src"); // Get the script that is currently loading. Again this creates a limitation where this should not be used in a button, and only before document.ready.

            document.writeln("<script type='text/javascript' src='" + scriptpath + "'></script>"); // Add the script to the document using writeln

            if (reAddLast)
            {
                $.import_js(callingScriptPath, false); // Call itself with the originating script to fix the order.
                throw 'Readding script to correct order: ' + scriptpath + ' < ' + callingScriptPath; // This halts execution of the originating script since it is getting reloaded. If you put a try / catch around the call to $.import_js you results will vary.
            }
            return true;
        }
        return false;
    }
});

Usage:

Fichier3:

var f3obj = "file3";

// Define other stuff
$(function(){
    f3obj = "file3docready";
});

Fichier2:

$.import_js('js/file3.js');
var f2obj = "file2";
$(function(){
    f2obj = "file2docready";
});

Fichier1:

$.import_js('js/file2.js');

// Use objects from file2 or file3
alert(f3obj); // "file3"
alert(f2obj); // "file2"

$(function(){
    // Use objects from file2 or file3 some more.
    alert(f3obj); //"file3docready"
    alert(f2obj); //"file2docready"
});
curlyhairedgenius
la source
C'est exactement ce que dit la réponse actuellement acceptée: tout simplement pas assez.
Cannicide le
La principale différence est que s'il y a une dépendance manquante, il ajoutera la balise de script pour la dépendance, puis ajoutera également la balise de script pour le fichier appelant et générera une erreur qui interrompra l'exécution. Cela provoque le traitement et l'exécution des scripts dans le bon ordre et supprime le besoin de rappels. Ainsi, le script dépendant sera chargé et exécuté avant le script appelant tout en prenant en charge l'imbrication.
curlyhairedgenius
Quant à «la tendance actuelle semble être de déprécier les demandes synchrones», cela n'est vrai que parce que de nombreux développeurs les ont mal utilisées et ont fait attendre inutilement les utilisateurs. Cependant, si la demande est naturellement synchrone, comme une instruction Include, alors Ajax synchrone simple est très bien. J'ai créé une fonction d'extension JavaScript générale qui exécute de manière synchrone des fonctions telles que la lecture d'un fichier qui ne fait pas partie de JavaScript en exécutant un fichier "serveur" PHP, et la trouve très utile lors de l'écriture d'applications en utilisant JavaScript. Aucun serveur Web local n'est nécessaire pour un tel Ajax.
David Spector
10

Il existe plusieurs façons d'implémenter des modules en Javascript, voici les 2 plus populaires:

Modules ES6

Les navigateurs ne prennent pas encore en charge ce système de modulation, donc pour pouvoir utiliser cette syntaxe, vous devez utiliser un bundler comme webpack. Il est préférable d'utiliser un bundler de toute façon, car cela peut combiner tous vos différents fichiers en un seul fichier (ou en couple). Cela servira les fichiers du serveur vers le client plus rapidement car chaque requête HTTP est accompagnée d'une surcharge associée. Ainsi, en réduisant la requête HTTP globale, nous améliorons les performances. Voici un exemple de modules ES6:

// main.js file

export function add (a, b) {
  return a + b;
}

export default function multiply (a, b) {
  return a * b;
}


// test.js file

import {add}, multiply from './main';   // for named exports between curly braces {export1, export2}
                                        // for default exports without {}

console.log(multiply(2, 2));  // logs 4

console.log(add(1, 2));  // logs 3

CommonJS (utilisé dans NodeJS)

Ce système de modulation est utilisé dans NodeJS. Vous ajoutez essentiellement vos exportations à un objet qui est appelé module.exports. Vous pouvez ensuite accéder à cet objet via un require('modulePath'). L'important ici est de réaliser que ces modules sont mis en cache, donc si vous require()un certain module deux fois, il retournera le module déjà créé.

// main.js file

function add (a, b) {
  return a + b;
}

module.exports = add;  // here we add our add function to the exports object


// test.js file

const add = require('./main'); 

console.log(add(1,2));  // logs 3
Willem van der Veen
la source
9

Je suis venu à cette question parce que je cherchais un moyen simple de maintenir une collection de plugins JavaScript utiles. Après avoir vu certaines des solutions ici, j'ai trouvé ceci:

  1. Configurez un fichier appelé "plugins.js" (ou extensions.js ou tout ce que vous voulez). Conservez vos fichiers de plug-in avec ce seul fichier maître.

  2. plugins.js aura un tableau appelé pluginNames[]que nous allons parcourir each(), puis ajouter une <script>balise à la tête de chaque plugin

//set array to be updated when we add or remove plugin files
var pluginNames = ["lettering", "fittext", "butterjam", etc.];

//one script tag for each plugin
$.each(pluginNames, function(){
    $('head').append('<script src="js/plugins/' + this + '.js"></script>');
});
  1. Appelez manuellement un seul fichier dans votre tête:
    <script src="js/plugins/plugins.js"></script>

MAIS:

Même si tous les plugins sont déposés dans la balise head comme ils le devraient, ils ne sont pas toujours exécutés par le navigateur lorsque vous cliquez sur la page ou que vous actualisez.

J'ai trouvé qu'il est plus fiable d'écrire simplement les balises de script dans une inclusion PHP. Vous n'avez qu'à l'écrire une fois et c'est tout autant de travail que d'appeler le plugin en utilisant JavaScript.

rgb_life
la source
@will, votre solution semble beaucoup plus propre que la mienne et je crains de ne pas avoir d'erreurs si j'utilise la mienne, car elle utilise .append (). Donc, pour l'utiliser, vous pouvez simplement appeler cette fonction une fois pour chaque fichier de plugin que vous souhaitez inclure?
rgb_life