library elementos div define crear con javascript requirejs dynamic-script-loading js-amd

javascript - elementos - ¿Cuál es la diferencia entre Require.js y simplemente crear un elemento<script> en el DOM?



requirejs config (4)

¿Cuál es la diferencia entre usar Require.JS y simplemente crear un elemento <script> en el DOM?

Mi comprensión de Require.JS es que ofrece la capacidad de cargar dependencias, pero ¿esto no se puede hacer simplemente creando un elemento <script> que cargue el archivo JS externo necesario?

Por ejemplo, supongamos que tengo la función doStuff() , que requiere la función needMe() . doStuff() está en el archivo externo do_stuff.js , mientras que needMe() está en el archivo externo need_me.js .

Haciendo esto la forma Require.JS:

define([''need_me''],function(){ function doStuff(){ //do some stuff needMe(); //do some more stuff } });

Haciendo esto simplemente creando un elemento script:

function doStuff(){ var scriptElement = document.createElement(''script''); scriptElement.src = ''need_me.js''; scriptElement.type = ''text/javascript''; document.getElementsByTagName(''head'')[0].appendChild(scriptElement); //do some stuff needMe(); //do some more stuff }

Ambos funcionan. Sin embargo, la segunda versión no requiere que cargue toda la biblioteca Require.js. Realmente no veo ninguna diferencia funcional ...


¿Qué ventajas ofrece Require.JS en comparación con simplemente crear un elemento en el DOM?

En su ejemplo, está creando la etiqueta del script de forma asíncrona, lo que significa que su función needMe() se invocará antes de que el archivo need_me.js termine de cargarse. Esto da como resultado excepciones no detectadas donde su función no está definida.

En cambio, para hacer que lo que sugieres realmente funcione, deberías hacer algo como esto:

function doStuff(){ var scriptElement = document.createElement(''script''); scriptElement.src = ''need_me.js''; scriptElement.type = ''text/javascript''; scriptElement.addEventListener("load", function() { console.log("script loaded - now it''s safe to use it!"); // do some stuff needMe(); //do some more stuff }, false); document.getElementsByTagName(''head'')[0].appendChild(scriptElement); }

Podría decirse que puede o no ser mejor usar un administrador de paquetes como RequireJS o utilizar una estrategia de JavaScript puro como se demostró anteriormente. Si bien su aplicación web puede cargarse más rápido, la funcionalidad y las funciones de invocación en el sitio serían más lentas, ya que implicaría esperar a que se carguen los recursos antes de que se pueda realizar esa acción.

Si una aplicación web se crea como una aplicación de una sola página, considere que las personas no volverán a cargar la página muy a menudo. En estos casos, precargar todo ayudaría a que la experiencia parezca más rápida cuando se use la aplicación. En estos casos, tiene razón, uno puede simplemente cargar todos los recursos simplemente incluyendo las etiquetas de script en el encabezado o cuerpo de la página.

Sin embargo, si se crea un sitio web o una aplicación web que siga el modelo más tradicional en el que se realiza una transición de página a página, lo que hace que los recursos se vuelvan a cargar, un enfoque de carga diferida puede ayudar a acelerar estas transiciones.


Algunas otras razones muy puntuales por las que usar RequireJS tiene sentido:

  1. Administrar sus propias dependencias rápidamente se desmorona para proyectos considerables.
  2. Puede tener tantos archivos pequeños como desee, y no tiene que preocuparse de hacer un seguimiento de las dependencias o del orden de carga.
  3. RequireJS hace posible escribir una aplicación modular completa sin tocar el objeto ventana.

Tomado de los comentarios de rmurphey aquí en este Gist .

Las capas de abstracción pueden ser una pesadilla para aprender y adaptarse, pero cuando cumplen un propósito y lo hacen bien, simplemente tienen sentido.


Aquí está el buen artículo en ajaxian.com sobre por qué usarlo:

RequireJS: carga de JavaScript asíncrona

  • algún tipo de # include / import / require
  • capacidad de cargar dependencias anidadas
  • facilidad de uso para el desarrollador, pero luego respaldado por una herramienta de optimización que ayuda a la implementación

Aquí hay un ejemplo más concreto.

Estoy trabajando en un proyecto con 60 archivos. Tenemos 2 modos diferentes de ejecutarlo.

  1. Cargue una versión concatenada, 1 archivo grande. (Producción)

  2. Carga los 60 archivos (desarrollo)

Estamos usando un cargador, así que solo tenemos un script en la página web

<script src="loader.js"></script>

El valor predeterminado es el modo n. ° 1 (cargando el único archivo concatenado grande). Para ejecutar el modo en # 2 (archivos separados) establecemos un indicador. Podría ser cualquier cosa. Una clave en la cadena de consulta. En este ejemplo, simplemente hacemos esto

<script>useDebugVersion = true;</script> <script src="loader.js"></script>

loader.js se parece a esto

if (useDebugVersion) { injectScript("app.js"); injectScript("somelib.js"); injectScript("someotherlib.js"); injectScript("anotherlib.js"); ... repeat for 60 files ... } else { injectScript("large-concatinated.js"); }

El script de compilación es solo un archivo .sh que se parece a esto

cat > large-concantinated.js app.js somelib.js someotherlib.js anotherlib.js

etc ...

Si se agrega un nuevo archivo, es probable que estemos usando el modo # 2 ya que estamos haciendo el desarrollo, tenemos que agregar una línea injectScript("somenewfile.js") a loader.js

Luego, para la producción, también tenemos que agregar somenewfile.js a nuestro script de compilación. Un paso que a menudo olvidamos y luego recibimos mensajes de error.

Al cambiar a AMD no tenemos que editar 2 archivos. El problema de mantener loader.js y la secuencia de comandos de compilación sincronizados desaparece. Usando r.js o webpack , puede leer el código para construir large-concantinated.js

También puede tratar con dependencias, por ejemplo, tenemos 2 archivos lib1.js y lib2.js cargados como este

injectScript("lib1.js"); injectScript("lib2.js");

lib2 necesita lib1. Tiene un código interno que hace algo así como

lib1Api.installPlugin(...);

Pero como los scripts inyectados se cargan de forma asíncrona, no hay garantía de que carguen en el orden correcto. Estos 2 scripts no son scripts de AMD, pero al usar require.js podemos contar sus dependencias

require.config({ paths: { lib1: ''./path/to/lib1'', lib2: ''./path/to/lib2'', }, shim: { lib1: { "exports": ''lib1Api'', }, lib2: { "deps": ["lib1"], }, } });

Yo nuestro módulo que usa lib1 hacemos esto

define([''lib1''], function(lib1Api) { lib1Api.doSomething(...); });

Ahora require.js inyectará los scripts para nosotros y no inyectará lib2 hasta que se haya cargado lib1, ya que le dijimos que lib2 depende de lib1. Tampoco iniciará nuestro módulo que usa lib1 hasta que se hayan cargado tanto lib2 como lib1.

Esto hace que el desarrollo sea agradable (sin pasos de compilación, sin preocuparse por el orden de carga) y hace que la producción sea agradable (no es necesario actualizar un script de compilación para cada script agregado).

Como bonificación adicional, podemos usar el plugin de babel de webpack para ejecutar Babel sobre el código para navegadores antiguos y tampoco tenemos que mantener ese script de compilación.

Tenga en cuenta que si Chrome (nuestro navegador de elección) comenzara a admitir la import de verdad, probablemente cambiaríamos a eso para el desarrollo, pero eso realmente no cambiaría nada. Todavía podríamos usar el paquete web para hacer un archivo concatenado y podríamos usarlo ejecutando babel sobre el código para todos los navegadores.

Todo esto se gana al no usar etiquetas de script y usar AMD