Existe-t-il quelque chose en JavaScript similaire à @import
CSS qui vous permet d'inclure un fichier JavaScript dans un autre fichier JavaScript?
script
balises ordonnées ?
Existe-t-il quelque chose en JavaScript similaire à @import
CSS qui vous permet d'inclure un fichier JavaScript dans un autre fichier JavaScript?
script
balises ordonnées ?
Réponses:
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.
Les modules ECMAScript (ES6) sont pris en charge dans Node.js depuis la v8.5, avec l' --experimental-modules
indicateur, 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.json
ou 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 .cjs
si 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";
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' .mjs
extension 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/
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
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.
Vous pouvez charger un script supplémentaire avec un appel AJAX puis l'utiliser eval
pour 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 eval
ouvre également la porte à des bugs, des hacks et des problèmes de sécurité.
Comme Dynamic Imports, vous pouvez charger un ou plusieurs scripts avec un fetch
appel 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)}`)
})
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.");
});
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' src
attribut 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 .
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.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
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.
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.
onreadystatechange
événement et readyState
proprié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
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.
...
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.js
fichier dans le HTML principal puis le script main.js
utilisé $.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).
jQuery.getScript
, de cette façon vous n'avez pas à vous soucier de l'écriture du plugin ...
script
élément à le head
fera fonctionner de manière asynchrone, sauf si le async
est spécifiquement défini sur false
.
"
, le code se cassera
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();
async: false
est censé être obsolète. Ce n'est pas! Comme l'indique votre citation, seuls les éléments liés à jqXHR le sont.
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 à cond
partir 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 à:
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");
js.onload = callback;
La déclaration import
est 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;
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)
}
script.onload = callback;
var
, la variable sera globale?
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-origin
tête dans sa réponse.
http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStuff/userjs/aagmfunctions.js
)
<script>
balise insérée , pas via XMLHttpRequest
.
const XMLHttpRequest = Components.Constructor("@mozilla.org/xmlextras/xmlhttprequest;1");
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 .
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 head
ou body
est 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/ .
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 $.getScript
méthode.
$.getScript("another_script.js");
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
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 @import
rè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.js
et 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(){}
}
};
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:
A = {};
A.func1 = function() {
console.log("func1");
}
A.func2 = function() {
console.log("func2");
}
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>
Cela devrait faire:
xhr = new XMLHttpRequest();
xhr.open("GET", "/soap/ajax/11.0/connection.js", false);
xhr.send();
eval(xhr.responseText);
eval
est ce qui ne va pas avec elle. De Crockford , " eval
est mauvais. La eval
fonction est la fonctionnalité la plus mal utilisée de JavaScript. Évitez-la. A eval
des alias. N'utilisez pas le Function
constructeur. Ne passez pas de chaînes à setTimeout
ou setInterval
." Si vous n'avez pas lu son "JavaScript: The Good Parts" alors sortez et faites-le maintenant. Vous allez ne le regretterez pas.
http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStuff/userjs/aagmfunctions.js
)
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:
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.
import
. Vous avez besoin d'un fichier HTML pour obtenir des informations d'un fichier js à un autre.
<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.
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 head
balise, etc. ne fonctionneront pas pour vous.
Heureusement, les Web Workers ont leur propre importScripts
fonction 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 importScripts
lui-même, mais avec quelques autres fonctionnalités utiles).
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 => {})
var pi = 3.14
. appeler la fonction loadJS () vialoadJs("pi.js").then(function(){ console.log(pi); });
La @import
syntaxe pour réaliser l'importation JavaScript de type CSS est possible en utilisant un outil tel que Mixture via leur .mix
type 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 .mix
fichiers:
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 .mix
fichier qui combine plusieurs .js
fichiers 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.js
que 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 .mix
fichier 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
var js = document.createElement("script");
js.type = "text/javascript";
js.src = jsFilePath;
document.body.appendChild(js);
body
n'existe pas encore ou ne peut pas être modifié. En outre, cela aide à expliquer les réponses.
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é».
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.
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);
};
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);
})();
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 $.getScript
mé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 .
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).
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 $.when
pour 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 $.when
sera 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.ready
n'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.ready
rappels 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.ready
rappel 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"
});
Il existe plusieurs façons d'implémenter des modules en Javascript, voici les 2 plus populaires:
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
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
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:
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.
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>');
});
<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.