¿Cómo incluyo un archivo JavaScript en otro archivo JavaScript?

Resuelto Alec Smart asked hace 15 años • 72 respuestas

¿Cómo incluyo un archivo JavaScript dentro de otro archivo JavaScript, similar a @importCSS?

Alec Smart avatar Jun 04 '09 18:06 Alec Smart
Aceptado

Las versiones antiguas de JavaScript no importaban, incluían ni requerían, por lo que se han desarrollado muchos enfoques diferentes para este problema.

Pero desde 2015 (ES6), JavaScript tiene el estándar de módulos ES6 para importar módulos en Node.js, que también es compatible con la mayoría de los navegadores modernos .

Para lograr compatibilidad con navegadores más antiguos, se pueden utilizar herramientas de compilación como Webpack y Rollup y/o herramientas de transpilación como Babel .

Módulos ES6

Los módulos ECMAScript (ES6) han sido compatibles con Node.js desde v8.5, con la --experimental-modulesbandera, y desde al menos Node.js v13.8.0 sin la bandera. Para habilitar "ESM" (en comparación con el sistema de módulos de estilo CommonJS anterior de Node.js ["CJS"]), puede usar "type": "module"o package.jsonasignar a los archivos la extensión .mjs. (De manera similar, los módulos escritos con el módulo CJS anterior de Node.js se pueden nombrar .cjssi su valor predeterminado es ESM).

Usando package.json:

{
    "type": "module"
}

Entonces module.js:

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

Entonces main.js:

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

Usando .mjs, tendrías module.mjs:

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

Entonces main.mjs:

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

Módulos ECMAScript en navegadores

Los navegadores han admitido la carga de módulos ECMAScript directamente (no se requieren herramientas como Webpack) desde Safari 10.1, Chrome 61, Firefox 60 y Edge 16. Consulte el soporte actual en caniuse . No es necesario utilizar .mjsla extensión de Node.js; Los navegadores ignoran por completo las extensiones de archivos en los módulos/scripts.

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

Leer más en https://jakearchibald.com/2017/es-modules-in-browsers/

Importaciones dinámicas en navegadores

Las importaciones dinámicas permiten que el script cargue otros scripts según sea necesario:

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

Lea más en https://developers.google.com/web/updates/2017/11/dynamic-import

Node.js requiere

El estilo de módulo CJS más antiguo, todavía ampliamente utilizado en Node.js, es module.exports/require system.

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

Hay otras formas de que JavaScript incluya contenidos JavaScript externos en navegadores que no requieren preprocesamiento.

Cargando AJAX

Puede cargar un script adicional con una llamada AJAX y luego usarlo evalpara ejecutarlo. Esta es la forma más sencilla, pero está limitada a su dominio debido al modelo de seguridad de la zona de pruebas de JavaScript. El uso evaltambién abre la puerta a errores, hackeos y problemas de seguridad.

Recuperar carga

Al igual que las importaciones dinámicas, puede cargar uno o varios scripts con una fetchllamada mediante promesas para controlar el orden de ejecución de las dependencias de los scripts mediante la biblioteca 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)}`)
})

Cargando jQuery

La biblioteca jQuery proporciona funcionalidad de carga en una línea :

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

Carga de secuencias de comandos dinámicas

Puede agregar una etiqueta de secuencia de comandos con la URL del script en el HTML. Para evitar la sobrecarga de jQuery, esta es una solución ideal.

El script puede incluso residir en un servidor diferente. Además, el navegador evalúa el código. La <script>etiqueta se puede insertar en la página web <head>o insertarse justo antes de la </body>etiqueta de cierre.

Aquí hay un ejemplo de cómo esto podría funcionar:

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

Esta función agregará una nueva <script>etiqueta al final de la sección de encabezado de la página, donde el srcatributo se establece en la URL que se proporciona a la función como primer parámetro.

Ambas soluciones se analizan e ilustran en JavaScript Madness: carga dinámica de scripts .

Detectar cuándo se ha ejecutado el script

Ahora bien, hay un gran problema que debes conocer. Hacer eso implica que cargas el código de forma remota . Los navegadores web modernos cargarán el archivo y seguirán ejecutando el script actual porque cargan todo de forma asincrónica para mejorar el rendimiento. (Esto se aplica tanto al método jQuery como al método de carga manual de scripts dinámicos).

Significa que si usa estos trucos directamente, no podrá usar el código recién cargado en la siguiente línea después de haber solicitado que se cargue , porque aún se estará cargando.

Por ejemplo: my_lovely_script.jscontiene 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

Luego recargas la página presionando F5. ¡Y funciona! Confuso...

Entonces, ¿qué hacer al respecto?

Bueno, puedes usar el truco que sugiere el autor en el enlace que te di. En resumen, para las personas que tienen prisa, utiliza un evento para ejecutar una función de devolución de llamada cuando se carga el script. Entonces puedes poner todo el código usando la biblioteca remota en la función de devolución de llamada. Por ejemplo:

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

Luego escribe el código que desea usar DESPUÉS de cargar el script en una función lambda :

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

Luego ejecutas todo eso:

loadScript("my_lovely_script.js", myPrettyCode);

Tenga en cuenta que el script puede ejecutarse después de que se haya cargado el DOM o antes, dependiendo del navegador y de si incluyó la línea script.async = false;. Hay un gran artículo sobre la carga de Javascript en general que trata esto.

Fusión/preprocesamiento de código fuente

Como se menciona al principio de esta respuesta, muchos desarrolladores utilizan herramientas de compilación/transpilación como Parcel, Webpack o Babel en sus proyectos, lo que les permite utilizar la próxima sintaxis de JavaScript, proporcionar compatibilidad con versiones anteriores para navegadores más antiguos, combinar archivos, minimizar, realizar división de código, etc.

Bite code avatar Jun 04 '2009 12:06 Bite code

Si alguien busca algo más avanzado, pruebe RequireJS . Obtendrá beneficios adicionales como administración de dependencias, mejor simultaneidad y evitará la duplicación (es decir, recuperar un script más de una vez).

Puede escribir sus archivos JavaScript en "módulos" y luego hacer referencia a ellos como dependencias en otros scripts. O puede utilizar RequireJS como una solución sencilla de "buscar este script".

Ejemplo:

Defina dependencias como módulos:

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

implementación.js es su archivo JavaScript "principal" que depende de some-dependency.js

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

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

Extracto del archivo README de GitHub :

RequireJS carga archivos JavaScript simples, así como módulos más definidos. Está optimizado para su uso en el navegador, incluido un Web Worker, pero se puede utilizar en otros entornos de JavaScript, como Rhino y Node. Implementa la API del módulo asincrónico.

RequireJS utiliza etiquetas de script simples para cargar módulos/archivos, por lo que debería permitir una fácil depuración. Se puede utilizar simplemente para cargar archivos JavaScript existentes, de modo que pueda agregarlo a su proyecto existente sin tener que volver a escribir sus archivos JavaScript.

...

John Strickler avatar Jun 07 '2012 20:06 John Strickler

En realidad, existe una forma de cargar un archivo JavaScript de forma no asincrónica, por lo que podría usar las funciones incluidas en el archivo recién cargado inmediatamente después de cargarlo, y creo que funciona en todos los navegadores.

Debes usar jQuery.append()en el <head>elemento de tu página, es decir:

$("head").append($("<script></script>").attr("src", url));

/* Note that following line of code is incorrect because it doesn't escape the
 * HTML attribute src correctly and will fail if `url` contains special characters:
 * $("head").append('<script src="' + url + '"></script>');
 */

Sin embargo, este método también tiene un problema: si ocurre un error en el archivo JavaScript importado, Firebug (y también Firefox Error Console y Chrome Developer Tools ) informará su ubicación incorrectamente, lo cual es un gran problema si usa Firebug para rastrear Los errores de JavaScript bajan mucho (lo hago). Firebug simplemente no sabe acerca del archivo recién cargado por algún motivo, por lo que si ocurre un error en ese archivo, informa que ocurrió en su archivo HTML principal y tendrá problemas para descubrir el verdadero motivo del error.

Pero si eso no es un problema para usted, entonces este método debería funcionar.

De hecho, escribí un complemento jQuery llamado $.import_js() que usa este método:

(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></script').attr('src', script));
                import_js_imported.push(script);
            }
        }
    });
    
})(jQuery);

Entonces, todo lo que necesitas hacer para importar JavaScript es:

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

También hice una prueba sencilla para esto en el Ejemplo .

Incluye un main.jsarchivo en el HTML principal y luego el script lo main.jsutiliza $.import_js()para importar un archivo adicional llamado included.js, que define esta función:

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

Y justo después de incluirlo included.js, hello()se llama a la función y recibe la alerta.

(Esta respuesta es en respuesta al comentario de e-satis).

Kipras avatar Apr 28 '2011 15:04 Kipras

Otra forma, que en mi opinión es mucho más limpia, es realizar una solicitud Ajax sincrónica en lugar de utilizar una <script>etiqueta. Así es también como maneja Node.js.

Aquí hay un ejemplo 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);
        }
    });
}

Luego puedes usarlo en tu código como normalmente usarías un include:

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

Y poder llamar a una función desde el script requerido en la siguiente línea:

subscript.doSomethingCool(); 
Ariel avatar Sep 08 '2011 18:09 Ariel