C'è qualcosa in JavaScript simile a @import
CSS che ti consente di includere un file JavaScript all'interno di un altro file JavaScript?
script
tag ordinati ?
C'è qualcosa in JavaScript simile a @import
CSS che ti consente di includere un file JavaScript all'interno di un altro file JavaScript?
script
tag ordinati ?
Risposte:
Le vecchie versioni di JavaScript non avevano importazione, inclusione o richiesta, quindi sono stati sviluppati molti approcci diversi a questo problema.
Ma dal 2015 (ES6), JavaScript ha lo standard di moduli ES6 per importare moduli in Node.js, che è supportato anche dalla maggior parte dei browser moderni .
Per la compatibilità con i browser più vecchi, crea strumenti come Webpack e Rollup e / o strumenti di transpilation come Babel .
I moduli ECMAScript (ES6) sono supportati in Node.js dalla v8.5, con il --experimental-modules
flag e almeno da Node.js v13.8.0 senza il flag. Per abilitare "ESM" (vs. precedente sistema di moduli CommonJS stile di Node.js [ "CJS"]) si sia per uso "type": "module"
in package.json
o dare i file l'estensione .mjs
. (Allo stesso modo, i moduli scritti con il precedente modulo CJS di Node.js possono essere nominati .cjs
se il tuo valore predefinito è ESM.)
Utilizzando package.json
:
{
"type": "module"
}
Quindi module.js
:
export function hello() {
return "Hello";
}
Quindi main.js
:
import { hello } from './module.js';
let val = hello(); // val is "Hello";
Usando .mjs
, avresti module.mjs
:
export function hello() {
return "Hello";
}
Quindi main.mjs
:
import { hello } from './module.mjs';
let val = hello(); // val is "Hello";
I browser hanno supportato il caricamento diretto dei moduli ECMAScript (non sono richiesti strumenti come Webpack) da Safari 10.1, Chrome 61, Firefox 60 e Edge 16. Controlla l'attuale supporto su caniuse . Non è necessario utilizzare l' .mjs
estensione Node.js ; i browser ignorano completamente le estensioni dei file su moduli / script.
<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);
}
Maggiori informazioni su https://jakearchibald.com/2017/es-modules-in-browsers/
Le importazioni dinamiche consentono allo script di caricare altri script secondo necessità:
<script type="module">
import('hello.mjs').then(module => {
module.hello('world');
});
</script>
Maggiori informazioni su https://developers.google.com/web/updates/2017/11/dynamic-import
Il vecchio stile del modulo CJS, ancora ampiamente utilizzato in Node.js, è il sistema module.exports
/require
.
// mymodule.js
module.exports = {
hello: function() {
return "Hello";
}
}
// server.js
const myModule = require('./mymodule');
let val = myModule.hello(); // val is "Hello"
Esistono altri modi in cui JavaScript può includere contenuti JavaScript esterni nei browser che non richiedono la preelaborazione.
È possibile caricare uno script aggiuntivo con una chiamata AJAX e quindi utilizzarlo eval
per eseguirlo. Questo è il modo più semplice, ma è limitato al tuo dominio a causa del modello di sicurezza sandbox JavaScript. L'utilizzo eval
apre anche la porta a bug, hack e problemi di sicurezza.
Come Dynamic Imports, puoi caricare uno o più script con una fetch
chiamata usando le promesse per controllare l'ordine di esecuzione delle dipendenze degli script usando la libreria 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 libreria jQuery fornisce funzionalità di caricamento su una riga :
$.getScript("my_lovely_script.js", function() {
alert("Script loaded but not necessarily executed.");
});
È possibile aggiungere un tag di script con l'URL dello script in HTML. Per evitare il sovraccarico di jQuery, questa è una soluzione ideale.
Lo script può persino risiedere su un altro server. Inoltre, il browser valuta il codice. Il <script>
tag può essere inserito nella pagina Web <head>
o inserito appena prima del </body>
tag di chiusura .
Ecco un esempio di come potrebbe funzionare:
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)
}
Questa funzione aggiungerà un nuovo <script>
tag alla fine della sezione head della pagina, dove ilsrc
attributo è impostato sull'URL che viene assegnato alla funzione come primo parametro.
Entrambe queste soluzioni sono discusse e illustrate in JavaScript Madness: Dynamic Script Loading .
Ora, c'è un grosso problema che devi conoscere. Ciò implica che si carica in remoto il codice . I browser Web moderni caricheranno il file e continueranno a eseguire lo script corrente perché caricano tutto in modo asincrono per migliorare le prestazioni. (Questo vale sia per il metodo jQuery che per il metodo di caricamento manuale degli script dinamici.)
Significa che se usi questi trucchi direttamente, non sarai in grado di usare il tuo nuovo codice caricato nella riga successiva dopo che hai chiesto di caricarlo , perché continuerà a caricarsi.
Ad esempio: my_lovely_script.js
contiene 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
Quindi ricarichi la pagina colpendo F5. E funziona! Confondere ...
Quindi cosa fare al riguardo?
Bene, puoi usare l'hack che l'autore suggerisce nel link che ti ho dato. In breve, per le persone che vanno di fretta, usa un evento per eseguire una funzione di richiamata quando viene caricato lo script. Quindi puoi inserire tutto il codice usando la libreria remota nella funzione di callback. Per esempio:
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);
}
Quindi scrivi il codice che vuoi usare DOPO che lo script è caricato in una funzione lambda :
var myPrettyCode = function() {
// Here, do whatever you want
};
Quindi esegui tutto ciò:
loadScript("my_lovely_script.js", myPrettyCode);
Si noti che lo script può essere eseguito dopo il caricamento del DOM o prima, a seconda del browser e se è stata inclusa la riga script.async = false;
. C'è un ottimo articolo sul caricamento di Javascript in generale che ne discute.
Come accennato all'inizio di questa risposta, molti sviluppatori usano strumenti di compilazione / transpilazione come Parcel, Webpack o Babel nei loro progetti, consentendo loro di utilizzare la sintassi JavaScript imminente, fornire compatibilità con le versioni precedenti per i browser più vecchi, combinare file, minimizzare, eseguire la suddivisione del codice ecc.
onreadystatechange
evento e readyState
proprietà). Inoltre, il caricamento dinamico degli script non beneficia degli scanner precaricati di Broswer. Consiglia questo articolo HTML5Rocks: html5rocks.com/en/tutorials/speed/script-loading
Se qualcuno è alla ricerca di qualcosa di più avanzato, prova RequireJS . Otterrai ulteriori vantaggi come la gestione delle dipendenze, una migliore concorrenza ed eviterai la duplicazione (ovvero il recupero di uno script più di una volta).
Puoi scrivere i tuoi file JavaScript in "moduli" e quindi fare riferimento a loro come dipendenze in altri script. Oppure puoi usare RequireJS come una semplice soluzione "vai a prendere questo script".
Esempio:
Definire le dipendenze come moduli:
alcuni-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
});
Implement.js è il tuo file JavaScript "principale" che dipende da some-dependency.js
require(['some-dependency'], function(dependency) {
//Your script goes here
//some-dependency.js is fetched.
//Then your script is executed
});
Estratto dal file README di GitHub :
RequireJS carica file JavaScript semplici e moduli più definiti. È ottimizzato per l'uso nel browser, incluso in un Web Worker, ma può essere utilizzato in altri ambienti JavaScript, come Rhino e Node. Implementa l'API del modulo asincrono.
RequireJS utilizza tag di script semplici per caricare moduli / file, quindi dovrebbe consentire un debug semplice. Può essere usato semplicemente per caricare file JavaScript esistenti, quindi è possibile aggiungerlo al progetto esistente senza dover riscrivere i file JavaScript.
...
In realtà c'è un modo per caricare un file JavaScript no in modo asincrono, così si potrebbe utilizzare le funzioni incluse nella vostra destra file appena caricato dopo il caricamento di esso, e penso che funziona in tutti i browser.
Devi utilizzare jQuery.append()
l' <head>
elemento della tua pagina, ovvero:
$("head").append('<script type="text/javascript" src="' + script + '"></script>');
Tuttavia, questo metodo ha anche un problema: se si verifica un errore nel file JavaScript importato, Firebug (e anche Firefox Error Console e Chrome Developer Tools ) segnalerà la sua posizione in modo errato, il che è un grosso problema se si utilizza Firebug per tracciare Errori JavaScript molto giù (lo faccio). Firebug semplicemente non conosce il file appena caricato per qualche motivo, quindi se si verifica un errore in quel file, segnala che si è verificato nel tuo HTML principale file e avrai difficoltà a scoprire il vero motivo dell'errore.
Ma se questo non è un problema per te, allora questo metodo dovrebbe funzionare.
In realtà ho scritto un plugin jQuery chiamato $ .import_js () che utilizza questo metodo:
(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);
Quindi tutto ciò che dovresti fare per importare JavaScript è:
$.import_js('/path_to_project/scripts/somefunctions.js');
Ho anche fatto un semplice test per questo in Esempio .
Include un main.js
file nell'HTML principale e quindi lo script main.js
utilizzato $.import_js()
per importare un file aggiuntivo chiamato included.js
, che definisce questa funzione:
function hello()
{
alert("Hello world!");
}
E subito dopo l'inclusione included.js
, la hello()
funzione viene chiamata e viene visualizzato l'avviso.
(Questa risposta è in risposta al commento di e-satis).
jQuery.getScript
, in questo modo non devi preoccuparti di scrivere il plugin ...
script
elemento head
causerà l'esecuzione in modo asincrono, a meno che non async
sia specificatamente impostato su false
.
"
, il codice si interromperà
Un altro modo, che secondo me è molto più pulito, è fare una richiesta Ajax sincrona invece di usare un <script>
tag. Questo è anche il modo in cui Node.js gestisce.
Ecco un esempio usando 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);
}
});
}
Puoi quindi usarlo nel tuo codice come di solito usi un include:
require("/scripts/subscript.js");
Ed essere in grado di chiamare una funzione dallo script richiesto nella riga successiva:
subscript.doSomethingCool();
async: false
presumibilmente è deprecato. Non è! Come afferma la tua citazione, solo le cose relative a jqXHR lo sono.
C'è una buona notizia per te. Molto presto sarai in grado di caricare facilmente il codice JavaScript. Diventerà un modo standard di importare moduli di codice JavaScript e farà parte del core JavaScript stesso.
Devi semplicemente scrivere import cond from 'cond.js';
per caricare una macro chiamata cond
da un file cond.js
.
Quindi non devi fare affidamento su alcun framework JavaScript né devi effettuare esplicitamente chiamate Ajax .
Fare riferimento a:
È possibile generare dinamicamente un tag JavaScript e aggiungerlo al documento HTML da un altro codice JavaScript. Questo caricherà il file JavaScript di destinazione.
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 dichiarazione import
è in ECMAScript 6.
Sintassi
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;
Forse puoi usare questa funzione che ho trovato in questa pagina Come posso includere un file JavaScript in un file 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 variabile sarà globale?
Ecco una versione sincrona senza 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);
}
Si noti che per far funzionare questo interdominio, il server dovrà impostare l' allow-origin
intestazione nella sua risposta.
http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStuff/userjs/aagmfunctions.js
)
<script>
tag inserito , non tramite XMLHttpRequest
.
const XMLHttpRequest = Components.Constructor("@mozilla.org/xmlextras/xmlhttprequest;1");
Ho appena scritto questo codice JavaScript (usando Prototype per la manipolazione del 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);
}
});
})();
Uso:
<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 .
Ecco la versione generalizzata di come lo fa Facebook per il loro pulsante Mi piace onnipresente:
<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>
Se funziona per Facebook, funzionerà per te.
Il motivo per cui cerchiamo il primo script
elemento anziché head
o body
è perché alcuni browser non ne creano uno se mancano, ma abbiamo la garanzia di avere un script
elemento: questo. Maggiori informazioni su http://www.jspatterns.com/the-ridiculous-case-of-adding-a-script-element/ .
Se vuoi in puro JavaScript, puoi usare document.write
.
document.write('<script src="myscript.js" type="text/javascript"></script>');
Se si utilizza la libreria jQuery, è possibile utilizzare il $.getScript
metodo
$.getScript("another_script.js");
Puoi anche assemblare i tuoi script usando PHP :
File 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 maggior parte delle soluzioni mostrate qui implica un caricamento dinamico. Stavo invece cercando un compilatore che assemblava tutti i file dipendenti in un unico file di output. Lo stesso dei preprocessori Less / Sass si occupano del CSS@import
regola . Dal momento che non ho trovato nulla di decente di questo tipo, ho scritto un semplice strumento per risolvere il problema.
Quindi ecco il compilatore, https://github.com/dsheiko/jsic , che sostituisce in $import("file-path")
modo sicuro il contenuto del file richiesto. Ecco il plugin Grunt corrispondente : https://github.com/dsheiko/grunt-jsic .
Sul ramo master jQuery, concatenano semplicemente i file di origine atomici in uno singolo iniziando con intro.js
e finendo con outtro.js
. Questo non mi va bene in quanto non offre flessibilità nella progettazione del codice sorgente. Scopri come funziona con jsic:
src / main.js
var foo = $import("./Form/Input/Tel");
src / Form / Input / Tel.js
function() {
return {
prop: "",
method: function(){}
}
}
Ora possiamo eseguire il compilatore:
node jsic.js src/main.js build/mail.js
E ottieni il file combinato
build / main.js
var foo = function() {
return {
prop: "",
method: function(){}
}
};
Se la tua intenzione di caricare il file JavaScript utilizza le funzioni del file importato / incluso , puoi anche definire un oggetto globale e impostare le funzioni come oggetti oggetto. Per esempio:
A = {};
A.func1 = function() {
console.log("func1");
}
A.func2 = function() {
console.log("func2");
}
A.func1();
A.func2();
Devi solo fare attenzione quando includi gli script in un file HTML. L'ordine dovrebbe essere come di seguito:
<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>
Questo dovrebbe fare:
xhr = new XMLHttpRequest();
xhr.open("GET", "/soap/ajax/11.0/connection.js", false);
xhr.send();
eval(xhr.responseText);
eval
è ciò che c'è di sbagliato con esso. Da Crockford , " eval
è malvagio. La eval
funzione è la caratteristica più abusata di JavaScript. Evitatela. eval
Ha alias. Non usare il Function
costruttore. Non passare stringhe a setTimeout
o setInterval
." Se non hai letto il suo "JavaScript: le parti buone", esci e fallo subito. Non te ne pentirai.
http://web.archive.org/web/20140905044059/http://www.howtocreate.co.uk/operaStuff/userjs/aagmfunctions.js
)
O piuttosto che includere in fase di esecuzione, utilizzare uno script per concatenare prima del caricamento.
Uso i pignoni (non so se ce ne sono altri). Costruisci il tuo codice JavaScript in file separati e includi i commenti che vengono elaborati dal motore Sprockets come include. Per lo sviluppo è possibile includere i file in sequenza, quindi per la produzione unirli ...
Guarda anche:
Ho avuto un semplice problema, ma sono rimasto sconcertato dalle risposte a questa domanda.
Ho dovuto usare una variabile (myVar1) definita in un file JavaScript (myvariables.js) in un altro file JavaScript (main.js).
Per questo ho fatto come di seguito:
Ho caricato il codice JavaScript nel file HTML, nell'ordine corretto, myvariables.js prima, quindi 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
// ...
}
// ...
Come hai visto, avevo usato una variabile in un file JavaScript in un altro file JavaScript, ma non avevo bisogno di includerne uno in un altro. Avevo solo bisogno di assicurarmi che il primo file JavaScript caricato prima del secondo file JavaScript e, le variabili del primo file JavaScript siano accessibili automaticamente nel secondo file JavaScript.
Questo mi ha salvato la giornata. Spero che questo possa essere d'aiuto.
import
. È necessario un file HTML per ottenere elementi da un file js a un altro.
<script>
tag. Questo può aiutare con l'organizzazione. Questa risposta non è semplicemente la domanda posta e non è l'ideale in questo contesto.
Nel caso in cui tu stia utilizzando Web Worker e desideri includere script aggiuntivi nell'ambito del lavoratore, le altre risposte fornite sull'aggiunta di script al head
tag, ecc. Non funzioneranno per te.
Fortunatamente, i Web Worker hanno una propria importScripts
funzione che è una funzione globale nell'ambito del Web Worker, nativa del browser stesso in quanto fa parte delle specifiche .
In alternativa, come evidenzia la seconda risposta più votata alla tua domanda , RequireJS può anche gestire gli script inclusi in un Web Worker (probabilmente chiamando importScripts
se stesso, ma con alcune altre utili funzionalità).
In linguaggio moderno con la spunta se lo script è già stato caricato sarebbe:
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)
});
}
Utilizzo (asincrono / attendi):
try { await loadJs("https://.../script.js") }
catch(error) {console.log(error)}
o
await loadJs("https://.../script.js").catch(err => {})
Utilizzo (promessa):
loadJs("https://.../script.js").then(res => {}).catch(err => {})
var pi = 3.14
. chiama la funzione loadJS () tramiteloadJs("pi.js").then(function(){ console.log(pi); });
La @import
sintassi per ottenere l'importazione JavaScript simile a CSS è possibile utilizzando uno strumento come Mixture tramite il loro .mix
tipo di file speciale (vedi qui ). Immagino che l'applicazione usi semplicemente uno dei metodi sopra citati, anche se non lo so.
Dalla documentazione Mixture sui .mix
file:
I file mix sono semplicemente file .js o .css con .mix. nel nome del file. Un file mix semplicemente estende la funzionalità di un normale file di stile o di script e consente di importare e combinare.
Ecco un .mix
file di esempio che combina più .js
file in uno:
// 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";
La miscela genera questo come scripts-global.js
e anche come versione minimizzata ( scripts-global.min.js
).
Nota: non sono in alcun modo affiliato con Mixture, a parte l'utilizzo come strumento di sviluppo front-end. Mi sono imbattuto in questa domanda quando ho visto un .mix
file JavaScript in azione (in una delle piastre della caldaia Mixture) e ne sono stato un po 'confuso ("puoi farlo?" Ho pensato tra me e me). Poi mi sono reso conto che si trattava di un tipo di file specifico dell'applicazione (un po 'deludente, d'accordo). Tuttavia, ho pensato che la conoscenza potesse essere utile per gli altri.
AGGIORNAMENTO : Mixture ora è gratuito (offline).
AGGIORNAMENTO : La miscela è ora interrotta. I vecchi rilasci di miscele sono ancora disponibili
var js = document.createElement("script");
js.type = "text/javascript";
js.src = jsFilePath;
document.body.appendChild(js);
body
non esiste ancora o non può essere modificato. Inoltre, aiuta a spiegare le risposte.
Il mio solito metodo è:
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);
}
Funziona benissimo e non usa ricariche di pagine per me. Ho provato il metodo AJAX (una delle altre risposte) ma non sembra funzionare altrettanto bene per me.
Ecco una spiegazione di come funziona il codice per coloro che sono curiosi: essenzialmente, crea un nuovo tag di script (dopo il primo) dell'URL. Lo imposta in modalità asincrona in modo che non blocchi il resto del codice, ma chiama un callback quando readyState (lo stato del contenuto da caricare) cambia in "caricato".
Sebbene queste risposte siano ottime, esiste una semplice "soluzione" che esiste da quando esiste il caricamento degli script e coprirà il 99,999% dei casi d'uso della maggior parte delle persone. Includi solo lo script di cui hai bisogno prima dello script che lo richiede. Per la maggior parte dei progetti non ci vuole molto per determinare quali script sono necessari e in quale ordine.
<!DOCTYPE HTML>
<html>
<head>
<script src="script1.js"></script>
<script src="script2.js"></script>
</head>
<body></body>
</html>
Se script2 richiede script1, questo è davvero il modo più semplice per fare qualcosa del genere. Sono molto sorpreso che nessuno l'abbia sollevato, in quanto è la risposta più ovvia e più semplice che verrà applicata in quasi ogni singolo caso.
Ho scritto un modulo semplice che automatizza il lavoro di importazione / inclusione di script del modulo in JavaScript. Per una spiegazione dettagliata del codice, fare riferimento al post sul blog JavaScript richiede / importa / include moduli .
// ----- 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);
};
Questo script aggiungerà un file JavaScript all'inizio di qualsiasi altro <script>
tag:
(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);
})();
C'è anche Head.js . È molto facile gestire:
head.load("js/jquery.min.js",
"js/jquery.someplugin.js",
"js/jquery.someplugin.css", function() {
alert("Everything is ok!");
});
Come vedi, è più semplice di Require.js e conveniente come il $.getScript
metodo jQuery . Ha anche alcune funzionalità avanzate, come il caricamento condizionale, il rilevamento delle funzionalità e molto altro .
Ci sono molte potenziali risposte a questa domanda. La mia risposta si basa ovviamente su alcuni di essi. Questo è quello che ho finito dopo aver letto tutte le risposte.
Il problema con $.getScript
e in realtà qualsiasi altra soluzione che richiede un callback al termine del caricamento è che se si hanno più file che lo utilizzano e dipendono l'uno dall'altro non si ha più modo di sapere quando tutti gli script sono stati caricati (una volta nidificati in più file).
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...
});
Hai ragione quando dici che potresti specificare Ajax da eseguire in modo sincrono o usare XMLHttpRequest , ma la tendenza attuale sembra essere quella di deprecare le richieste sincrone, quindi potresti non ottenere il supporto completo del browser ora o in futuro.
Potresti provare a usare $.when
per controllare una matrice di oggetti differiti, ma ora lo stai facendo in ogni file e file2 sarà considerato caricato non appena $.when
viene eseguito non quando viene eseguita la richiamata, quindi file1 continua ancora l'esecuzione prima che venga caricato file3 . Questo ha ancora lo stesso problema.
Ho deciso di andare indietro invece che in avanti. Grazie document.writeln
. So che è un tabù, ma fintanto che viene usato correttamente funziona bene. Si finisce con il codice che può essere facilmente eseguito il debug, viene visualizzato correttamente nel DOM e può garantire l'ordine di caricamento corretto delle dipendenze.
Ovviamente puoi usare $ ("body"). Append (), ma non potrai più eseguire correttamente il debug.
NOTA: è necessario utilizzarlo solo durante il caricamento della pagina, altrimenti viene visualizzata una schermata vuota. In altre parole, posizionalo sempre prima / all'esterno di document.ready . Non ho provato a usarlo dopo che la pagina è stata caricata in un evento click o qualcosa del genere, ma sono abbastanza sicuro che fallirà.
Mi è piaciuta l'idea di estendere jQuery, ma ovviamente non è necessario.
Prima di chiamare document.writeln
, verifica che lo script non sia già stato caricato valutando tutti gli elementi dello script.
Presumo che uno script non sia completamente eseguito fino a quando il suo document.ready
evento non è stato eseguito. (So che document.ready
non è richiesto l'uso, ma molte persone lo usano e gestirlo è una salvaguardia.)
Quando vengono caricati i file aggiuntivi, i document.ready
callback verranno eseguiti nell'ordine sbagliato. Per risolvere questo problema quando uno script viene effettivamente caricato, lo script che lo ha importato viene reimportato e l'esecuzione viene interrotta. Ciò fa ora document.ready
eseguire il callback del file di origine dopo uno qualsiasi degli script che importa.
Invece di questo approccio potresti provare a modificare jQuery readyList
, ma questa sembra una soluzione peggiore.
Soluzione:
$.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;
}
});
Uso:
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"
});
Esistono diversi modi per implementare i moduli in Javascript, ecco i 2 più popolari:
I browser non supportano ancora questo sistema di modulazione, quindi per poter utilizzare questa sintassi è necessario utilizzare un bundler come il webpack. L'uso di un bundler è comunque meglio perché può combinare tutti i tuoi diversi file in un singolo file (o associato a coppie). Questo servirà i file dal server al client più velocemente perché ogni richiesta HTTP ha un sovraccarico associato con esso. Pertanto, riducendo la richiesta HTTP globale, miglioriamo le prestazioni. Ecco un esempio di moduli 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
Questo sistema di modulazione viene utilizzato in NodeJS. Fondamentalmente aggiungi le tue esportazioni a un oggetto che viene chiamato module.exports
. È quindi possibile accedere a questo oggetto tramite a require('modulePath')
. Importante qui è rendersi conto che questi moduli vengono memorizzati nella cache, quindi se require()
un determinato modulo due volte restituirà il modulo già creato.
// 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
Sono arrivato a questa domanda perché stavo cercando un modo semplice per mantenere una raccolta di plugin JavaScript utili. Dopo aver visto alcune delle soluzioni qui, ho pensato a questo:
Imposta un file chiamato "plugins.js" (o extensions.js o come vuoi). Mantieni i tuoi file plugin insieme a quel file principale.
plugins.js avrà un array chiamato pluginNames[]
che ripeteremo each()
, quindi aggiungere un <script>
tag in testa per ogni 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>
MA:
Anche se tutti i plugin vengono inseriti nel tag head come dovrebbero, non sempre vengono eseguiti dal browser quando si fa clic sulla pagina o si aggiorna.
Ho trovato più affidabile scrivere semplicemente i tag di script in un include PHP. Devi solo scriverlo una volta e questo è tanto lavoro quanto chiamare il plugin usando JavaScript.