¿Cómo puedo incluir un archivo JavaScript en otro archivo JavaScript?

¿Hay algo en JavaScript similar a @import en CSS que permita incluir un archivo JavaScript dentro de otro archivo JavaScript?

Solución

Las versiones antiguas de JavaScript no tenían import, include o require, por lo que se han desarrollado muchos enfoques diferentes para este problema.

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

Para la compatibilidad con los navegadores más antiguos, se pueden utilizar las herramientas build y/o transpilation.

Módulos ES6

Los módulos ECMAScript (ES6) han sido soportados en Node.js desde la v8.5, con la bandera --experimental-modules. Todos los archivos involucrados deben tener la extensión .mjs.

// module.mjs
export function hello() {
  return "Hello";
}
// main.mjs
import { hello } from 'module'; // or './module'
let val = hello();  // val is "Hello";

Módulos ECMAScript en los navegadores

Los navegadores tienen soporte para cargar módulos ECMAScript directamente (sin necesidad de herramientas como Webpack) desde Safari 10.1, Chrome 61, Firefox 60 y Edge 16. Compruebe el soporte actual en caniuse.

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

Más información en https://jakearchibald.com/2017/es-modules-in-browsers/

Importaciones dinámicas en los 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>

Más información en https://developers.google.com/web/updates/2017/11/dynamic-import

Node.js require

El viejo estilo de importar módulos, todavía muy utilizado en Node.js, es el 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"   

Existen otras formas de incluir contenidos externos de JavaScript en los navegadores que no requieren preprocesamiento.

Carga AJAX

Puedes cargar un script adicional con una llamada AJAX y luego usar eval para ejecutarlo. Esta es la forma más directa, pero está limitada a su dominio debido al modelo de seguridad de la caja de arena de JavaScript. El uso de eval también abre la puerta a bugs, hacks y problemas de seguridad.

Carga de Fetch

Al igual que las importaciones dinámicas, puedes cargar uno o varios scripts con una llamada fetch usando promesas para controlar el orden de ejecución de las dependencias de los scripts usando la librería 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)}`)
})

Carga de jQuery

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

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

Carga dinámica de scripts

Puedes añadir una etiqueta de script 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 etiqueta <script> puede ser inyectada en el de la página web, o insertada justo antes de la etiqueta de cierre.

Este es un ejemplo de cómo 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 añadirá una nueva etiqueta <script> al final de la sección head de la página, donde el atributo src se establece en la URL que se da a la función como primer parámetro.

Ambas soluciones se discuten e ilustran en JavaScript Madness: Dynamic Script Loading.

Detección de la ejecución del script

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

Significa que si usas estos trucos directamente, no podrás usar tu código recién cargado la siguiente línea después de haber pedido que se cargue, porque todavía se estará cargando.

Por ejemplo: 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

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

¿Entonces qué hacer al respecto?

Bueno, puedes usar el hack que sugiere el autor en el enlace que te di. En resumen, para la gente con prisa, utiliza un evento para ejecutar una función callback cuando se carga el script. Así puedes poner todo el código que usa la librería remota en la función callback. 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 escribes el código que quieres 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);

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

Fusión de código fuente/preprocesamiento

Como se mencionó en la parte superior de esta respuesta, muchos desarrolladores utilizan herramientas de construcció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 navegadores antiguos, combinar archivos, minificar, realizar la división del código, etc.

Comentarios (31)

Es posible generar dinámicamente una etiqueta JavaScript y añadirla al documento HTML desde dentro de otro código JavaScript. Esto cargará el archivo de JavaScript dirigido.

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");
Comentarios (13)

Tal vez puedas usar esta función que encontré en esta página ¿Cómo incluyo un archivo JavaScript en un archivo 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)
}
Comentarios (7)