Comunidad de diseño web y desarrollo en internet online

Carga dinámica de librerías Javascript con LibraryManager

Hace pocos meses me adentré en lo que envuelve Javascript y la web 2.0 con AJAX, viniendo de programar en ActionScript. Quise ser valiente y seguir utilizando el concepto de clase, pequeños módulos (o no), de código reutilizable y que facilitan el mantenimiento de forma considerable. Al conjunto de estos módulos se les llama librería.

Para importar una librería de una aplicación en tu página web, puedes escribir lo siguiente en el tag "head":

Código :

<script src="includes/javascript/Archivo1.js" type="text/javascript"></script>
<script src="includes/javascript/Archivo2.js" type="text/javascript"></script>
<script src="includes/javascript/Archivo3.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="includes/styles/estilo.css"/>

Ahora imagina que tienes un buen número de aplicaciones con sus correspondientes librerías y desconoces cuando las vas a necesitar. El código de arriba está muy bien si tienes previsto crear contenido estático, pero puedes necesitar cargar cierta aplicación en un momento dado, por eso utilizaremos la carga dinámica.

Una opción es incrustar el tag <script> mediante createElement/appendChild a <head>, pero corres el riesgo de que, en caso de que Archivo2 necesite instanciar a Archivo1, éste último todavía no se haya cargado. Por eso he creado LibraryManager, una clase en Javascript que facilita la carga dinámica haciendo uso de peticiones AJAX (comentar que he utilizado el Framework Prototype). Aquí el código:

Código :

var LibraryManager = Class.create({
    initialize: function(){
        var carpetaBase = "includes/";
        this._rutaArchivos = new Array(); // ruta según tipo de archivo (manejado por extensión)
        this._rutaArchivos["css"] = carpetaBase + "styles/";
        this._rutaArchivos["js"] = carpetaBase + "javascript/";
        this._archivosCargandose = new Array(); // archivos que actualmente se están cargando
        this._archivosCargados = new Array(); // archivos que ya se han cargado
    },
   /**
    * Método reiterativo. Carga un archivo por cada llamada.
    * @param {Object} libreria (Array) -> Listado de archivos a cagar
    * @param {Object} callback (Function) -> Se llamará esta función al terminar la carga de todos los archivos
    */
    cargar: function(libreria, callback){
        if (libreria.length == 0) 
            callback.call();
        else {
            // si no se ha cargado
            if (!this._archivosCargados[libreria.first()]) {
                var scope = this;
                // si ya se está cargando, esperar a que esté listo antes de continuar
                // (ésto es útil en caso de cargar múltiples librerías a la vez que requieran ese mismo archivo)
                if (this._archivosCargandose[libreria.first()]) {
                    var interval = setInterval(function(){
                        if (scope._archivosCargados[libreria.first()]) {
                            libreria.shift();
                            clearInterval(interval);
                            scope.cargar(libreria, callback);
                        }
                    }, 10);
                }
                else {
                    var tipo = libreria.first().split(".").last();
                    this._archivosCargandose[libreria.first()] = true;
                    switch (tipo) {
                        case "css":
                            var nodoCss = document.createElement('link');
                            with (nodoCss) {
                                type = 'text/css';
                                rel = 'stylesheet';
                                media = 'screen';
                            }
                            nodoCss.href = this._rutaArchivos[tipo] + libreria.first();
                            document.getElementsByTagName("head")[0].appendChild(nodoCss);
                            this._archivosCargados[libreria.first()] = true;
                            libreria.shift();
                            this.cargar(libreria, callback);
                            break;
                        case "js":
                            new Ajax.Request(this._rutaArchivos[tipo] + libreria.first(), {
                                method: 'get',
                                onComplete: function(){
                                    scope._archivosCargados[libreria.first()] = true;
                                    libreria.shift();
                                    scope.cargar(libreria, callback);
                                }
                            });
                            break;
                        default:
                            libreria.shift();
                            this.cargar(libreria, callback);
                    }
                }
            }
            // si ya se ha cargado
            else {
                libreria.shift();
                this.cargar(libreria, callback);
            }
        }
    }
});
var libraryManager = new LibraryManager();

Resumidamente, la clase distingue entre los archivos *.css y *.js. Los estilos los carga sin necesidad de hacer ninguna petición AJAX al no ser necesario. Hasta que no termina con un archivo, no pasa al siguiente. Controla que un archivo no se cargue más de una vez. Permite realizar cargas múltiples al mismo tiempo controlando si un archivo ya se está cargando (por ejemplo, si para otra aplicación ya se encuentra cargando ese archivo) esperando a que esté listo y continuar con el siguiente.

Para utilizarla solo debes importarla y cuando necesites hacer una carga, llamar a su método cargar (ya se crea una instancia llamada libraryManager en el propio archivo de la clase, por lo que no es necesario hacer nada más). Ejemplo:

Código :

var libreria1 = ["estilos.css","1.js", "2.js"];
function cargada(){
   alert("¡Librería lista!");
   // aquí se podría instanciar a la clase principal de la aplicación
}
libraryManager.cargar(libreria1.slice(), cargada);

Eso es todo por hoy. ¡Gracias por su atención y hasta la próxima! ^^

(Por supuesto, cualquier crítica constructiva será bienvenida)

Archivos de ejemplo: http://www.cristalab.com/images/tips/ajax/LibraryManager/LibraryManager.rar

¿Sabes SQL? ¿No-SQL? Aprende MySQL, PostgreSQL, MongoDB, Redis y más con el Curso Profesional de Bases de Datos que empieza el martes, en vivo.

Publica tu comentario

o puedes...

¿Estás registrado en Cristalab y quieres
publicar tu URL y avatar?

¿No estás registrado aún pero quieres hacerlo antes de publicar tu comentario?

Registrate