Codepoint

by Trentia Consulting

JavaScript : Carga dinámica de ficheros e inyección de dependencias.

Hoy en día las aplicaciones web cada vez gozan de más dinamismo al momento de interactuar con el usuario lo que implica más programación de Front-End.

Cada vez más se traslada parte de la lógica de negocio de las aplicaciones web para ejecutarse en la parte cliente de la aplicación. Esto es una carga para el ordenador del usuario cada vez mayor. Y cada vez es más necesario el considerar cargar de forma dinámica archivos JavaScript justo en el momento en que sea necesario para reservar los mínimos recursos que podamos y todo sea fluido y óptimo.

Si además combinamos este comportamiento añadiéndole un orden de carga y permitimos que se realicen ejecuciones concretas justo cuando se haya cargado el código, obtenemos una simple inyección de dependencias.

(function (window) {
    window.ModuleLoader = {
        _ModulesLength: 0,
        _ModulesLoaded: 0,
        AfterLoading: undefined,

        Load: function (data) {
            var _modules = data.Modules;
            this._ModulesLength = _modules.length;
            this.AfterLoading = data.AfterLoading;

            if (typeof data.BeforeLoading === 'function')
                data.BeforeLoading();

            for (var i = 0; i < this._ModulesLength; i++) {
                var script = document.createElement("script");
                script.type = "text/javascript";

                if (script.readyState) {
                    if (_modules[i].callback !== undefined)
                        script.onreadystatechange = this.Execution(_modules[i].callback);
                } else {
                    if (_modules[i].callback !== undefined)
                        script.onload = this.Execution(_modules[i].callback);
                }

                script.src = (_modules[i].host || "") + _modules[i].url;
                document.getElementsByTagName("head")[0].appendChild(script);
            }
        },
        Execution: function (callback) {
            return function () {
                ModuleLoader._ModulesLoaded++;
                callback();

                if (ModuleLoader._ModulesLoaded >= ModuleLoader._ModulesLength)
                    ModuleLoader.AfterLoading();
            };
        }
    };
})(window);

El código anterior añade al objeto Window un objeto en su miembro “ModuleLoader” el cual tendrá una función llamada Load que espera un solo parámetro que será un “object” con esta forma:

 

{
   Modules : [
        	          {
           	    Host: string,
           	    url: string,
           	    callback: function
       		} 
   	   ],
            AfterLoading : function ,
            BeforeLoading : function 
}

Esto definirá los módulos que queremos cargar, que se traducen en ficheros y las acciones que queremos realizar después de la carga de los mismos. Estos Callbacks son la clave para resolver las dependencias necesarias para los módulos que pretendamos cargar.

También incorpora 2 Callbacks adicionales para ejecutar antes y después de la carga completa de todos los módulos especificados.

Todo esto nos permite cargar los ficheros JavaScript que necesitemos, así como las librerías JavaScript que fueran necesarias para el funcionamiento de los mismos.

Ahora expondremos un ejemplo claro cargando dos ejemplos de modulo que dependen uno de otro y a la vez dependen de Jquery :

ModuleLoader.Load({
    Modules : [
            // Cargamos Jquery
            {
                host: "https://ajax.googleapis.com/",
                url: "ajax/libs/jquery/2.2.2/jquery.min.js",
                callback: function () {
                    //Acciones de inicialización cuando Jquery quede cargado.

                }
            },
            {
                host: "",//Dejar en blanco host es como usar el host actual
                url: "OtroFichero.js",//Este modulo tiene una dependencia, en su interior utiliza otro modulo que esta en otro fichero. 
                //Cargamos el modulo y cuando se haya acabado de cargar, cargamos su dependencia
                callback: function () {
                    //  Hemos cargado los modulos en orden y realizaremos aquí las operaciones necesarias.
                }
            }
        ],
  AfterLoading: function () {
            alert("foo");
        },
        BeforeLoading: function () {
            alert("bar");
        }
});


//Fichero OtroFichero.js
(function (namespace) {

    ModuleLoader.Load({
                //En este fichero se encuentra el modulo del que depende OtroFichero.js
                Modules: [ 
                    {
                        host: "",//Cargamos el modulo dependiente. Cuando hayamos cargamos todas las dependencias
                        url: "FicheroModulo2.js",
                        callback: function () {
                            namespace.ModuleInstance = new Module(window.Module2);
                            alert(namespace.ModuleInstance.Result);
                        }
                    }
                ]
            });

    function Module(dependency) {
        this.Result = dependency.Calculation();
    }

})(window);



//Fichero FicheroModulo2.js
(function (parentObject) {
    parentObject.Module2 = { 
        Calculation : function () {
            return 2 + 2;
        }
    };
})(window);

 

Es muy importante comprender el orden en el que se ejecutan las cargas y cómo reacciona el código escrito en los módulos cargados. Por ejemplo no sería correcto intentar utilizar un modulo que no este cargado previamente, eso conduciría a errores en el código.

Así logramos cargar de forma dinámica todos los módulos necesarios para el buen funcionamiento de nuestra aplicación web. Esta implementación no utiliza ningún tipo de librería.

Es importante observar que los módulos que se cargan están implementados de una manera en concreto para que se comuniquen entre ellos correctamente.

Esta manera de implementar módulos permite agrupar los objetos en algo similar a los espacios de nombres conocidos en otros lenguajes.

Agregar comentario

Loading