Comment inclure un fichier JavaScript dans un autre fichier JavaScript?



Answers

Si quelqu'un cherche quelque chose de plus avancé, essayez RequireJS . Vous obtiendrez des avantages supplémentaires tels que la gestion des dépendances, une meilleure concomitance et éviter la duplication (c'est-à-dire récupérer 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éfinir 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 "principal" JavaScript 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 fichier GitHub README:

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, tels que Rhino et Node. Il implémente l'API du module asynchrone.

RequireJS utilise des balises de script simples pour charger les modules / fichiers, ce qui devrait faciliter le débogage. Il peut être utilisé simplement pour charger des fichiers JavaScript existants, de sorte que vous pouvez l'ajouter à votre projet existant sans avoir à réécrire vos fichiers JavaScript.

...

Question

Y at-il quelque chose dans JavaScript similaire à @import dans CSS qui vous permet d'inclure un fichier JavaScript dans un autre fichier JavaScript?




There are a lot of potential answers for this question. My answer is obviously based on a number of them. This is what I ended up with after reading through all the answers.

The problem with $.getScript and really any other solution that requires a callback when loading is complete is that if you have multiple files that use it and depend on each other you no longer have a way to know when all scripts have been loaded (once they are nested in multiple files).

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...
});

You are right when you say that you could specify Ajax to run synchronously or use XMLHttpRequest , but the current trend appears to be to deprecate synchronous requests, so you may not get full browser support now or in the future.

You could try to use $.when to check an array of deferred objects, but now you are doing this in every file and file2 will be considered loaded as soon as the $.when is executed not when the callback is executed, so file1 still continues execution before file3 is loaded. This really still has the same problem.

I decided to go backwards instead of forwards. Thank you document.writeln . I know it's taboo, but as long as it is used correctly this works well. You end up with code that can be debugged easily, shows in the DOM correctly and can ensure the order the dependencies are loaded correctly.

You can of course use $ ("body").append(), but then you can no longer debug correctly any more.

NOTE: You must use this only while the page is loading, otherwise you get a blank screen. In other words, always place this before / outside of document.ready . I have not tested using this after the page is loaded in a click event or anything like that, but I am pretty sure it'll fail.

I liked the idea of extending jQuery, but obviously you don't need to.

Before calling document.writeln , it checks to make sure the script has not already been loading by evaluating all the script elements.

I assume that a script is not fully executed until its document.ready event has been executed. (I know using document.ready is not required, but many people use it, and handling this is a safeguard.)

When the additional files are loaded the document.ready callbacks will get executed in the wrong order. To address this when a script is actually loaded, the script that imported it is re-imported itself and execution halted. This causes the originating file to now have its document.ready callback executed after any from any scripts that it imports.

Instead of this approach you could attempt to modify the jQuery readyList , but this seemed like a worse 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:

File3:

var f3obj = "file3";

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

File2:

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

File1:

$.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"
});



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 .




Better use the jQuery way. To delay the ready event, first call $.holdReady(true) . Example ( source ):

$.holdReady(true);
$.getScript("myplugin.js", function() {
    $.holdReady(false);
});



Or rather than including at run time, use a script to concatenate prior to upload.

I use Sprockets (I don't know if there are others). You build your JavaScript code in separate files and include comments that are processed by the Sprockets engine as includes. For development you can include files sequentially, then for production to merge them...

Voir également:




In modern language it would be

function loadJs( url ){
  return new Promise( resolve => {
    const script = document.createElement( "script" );
    script.src = url;
    script.onload = resolve;
    document.head.appendChild( script );
  });
}



La plupart des solutions présentées ici impliquent un chargement dynamique. Je cherchais plutôt un compilateur qui assemblerait tous les fichiers dépendants dans un seul fichier de sortie. Les mêmes que les préprocesseurs Less / Sass traitent de la @import CSS @import at-rule. Comme je n'ai rien trouvé de décent 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") par le contenu du fichier demandé en toute sécurité. Voici le plugin Grunt correspondant: https://github.com/dsheiko/grunt-jsic .

Sur la branche principale jQuery, ils concaténent simplement les fichiers sources atomiques en un seul en commençant par intro.js et en terminant par outtro.js . Cela ne me convient pas car cela 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 / Entrée / Tel.js

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

Maintenant, nous pouvons lancer 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(){}
    }
};



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

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

document.body.appendChild(js);



I had a simple issue, but I was baffled by responses to this question.

I had to use a variable (myVar1) defined in one JavaScript file (myvariables.js) in another JavaScript file (main.js).

For this I did as below:

Loaded the JavaScript code in the HTML file, in the correct order, myvariables.js first, then main.js:

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

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

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

File: myvariables.js

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

File: main.js

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

As you saw, I had use a variable in one JavaScript file in another JavaScript file, but I didn't need to include one in another. I just needed to ensure that the first JavaScript file loaded before the second JavaScript file, and, the first JavaScript file's variables are accessible in the second JavaScript file, automatically.

This saved my day. J'espère que ça aide.




Une autre façon, à mon avis beaucoup plus propre, est de faire une requête Ajax synchrone au lieu d'utiliser une <script> . Ce qui est également la manière dont Node.js gère.

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 le feriez habituellement avec un include:

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

Et être en mesure d'appeler une fonction du script requis dans la ligne suivante:

subscript.doSomethingCool(); 



I wrote a simple module that automates the job of importing/including module scripts in JavaScript. For detailed explanation of the code, refer to the blog post 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);
};



Il est possible de générer dynamiquement une balise JavaScript et de l'ajouter au document HTML à partir d'autres codes JavaScript. Cela va charger 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");



Peut-être que vous pouvez utiliser cette fonction que j'ai trouvée sur cette page Comment 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)
}



C'est peut-être la plus grande faiblesse de JavaScript à mon avis. Cela m'a permis de résoudre les problèmes au fil des ans avec le dépistage des dépendances. Quoi qu'il en soit, il semble que la seule solution pratique consiste à utiliser des scripts inclus dans le fichier HTML et donc horriblement rendre votre code JavaScript dépendant de l'utilisateur, y compris la source dont vous avez besoin et de rendre la réutilisation hostile.

Désolé si cela se présente comme une conférence;) C'est une (mauvaise) habitude à moi, mais je veux faire ce point.

Le problème revient au même que tout le reste avec le web, l'histoire de JavaScript. Il n'a vraiment pas été conçu pour être utilisé de la manière répandue dans laquelle il est utilisé aujourd'hui. Netscape fait un langage qui vous permettrait de contrôler un certain nombre de choses, mais ils n'envisageaient pas son utilisation répandue pour tant de choses telles qu'elles sont mises en place et pour une raison ou une autre il est développé à partir de là, sans aborder certaines faiblesses de la stratégie originale.

Ce n'est pas seul bien sûr. HTML n'a pas été conçu pour la page Web moderne; il a été conçu pour exprimer la logique d'un document, afin que les lecteurs (navigateurs du monde moderne) puissent l'afficher sous une forme applicable qui soit dans les capacités du système, et il a fallu des années pour trouver une solution (autre que les hacks de MS et Netscape) à venir. CSS résout ce problème, mais il a fallu beaucoup de temps pour que les gens l'utilisent plutôt que les techniques BAD établies. Il est arrivé cependant, l'éloge soit.

Espérons que JavaScript (surtout maintenant il fait partie de la norme) se développera pour prendre en compte le concept de modularité correcte (ainsi que d'autres choses) comme tout autre langage de programmation (existant) dans le monde et cette bêtise disparaîtra. Jusque-là, il faut juste ne pas l'aimer et le cogner, j'ai peur.




Cela devrait faire:

xhr = new XMLHttpRequest();
xhr.open("GET", "/soap/ajax/11.0/connection.js", false);
xhr.send();
eval(xhr.responseText);



Links