miércoles, 12 de junio de 2019

FrameWork Sencillo En VanillaScript O Algo Parecido


 Buenas, pues aquí estamos, a piñón con el desarrollo Web, aunque no es de mis pasiones a pesar de haberlo estudiado, más que nada porque no había otra cosa que estudiar, pues es importante para mi de cara al trabajo que desempeñaré. Espero que con experiencia algún día pueda saltar a otras ramas del desarrollo.

 Vale, pues lo que les traigo hoy aquí, es un sencillo framework con propósito meramente educativo, no se trata de reinventar la rueda, pero es importante tener un conocimiento de como funcionan esas cosas tipo Vue JS, React, Angulares y demás, que intentan facilitar el desarrollo en el frontend por medio de un marco de trabajo modular, que permita implementar la aplicación de forma organizada y modificar los módulos de éste sin mucho costo.

Etiquetado personalizado y componentes


 Este tipo de frames, los citados anteriormente, destacan por el uso de los llamados componentes, digamos que la filosofía es llevar la programación orientada objetos a límites insospechables en el front integrando objetos que tengan parte lógica, funcional, propiedades o atributos y una vista o plantilla que les confiera en resumen una independencia del resto de componentes pero que permita integrarse conjuntamente con todo.

 En el ejemplo que mostraré abajo de nuestro componente se trata simplemente de una estructura, que bien podría ser una clase, que muestra las características descritas. 


{ "etiqueta": {
                body: {
                    campo1: 'soy el campo1',
                    campo2: 'soy el campo2'
                },
                template: "<h1> {{ campo1 }} / {{ campo2 }} </h1>" }

 Como se puede apreciar arriba, hemos definido una propiedad template para nuestro componente que define como será visualmente, la plantilla, así como un nombre de etiqueta que representará al componente tanto en nuestro html como cuando nos queramos referir a éste en el programa.

 Dentro de la propiedad body estarán los atributos de la plantilla, que estarán vinculados al render, de esta forma se diseñará para que cuando trabajemos con el componente nos olvidemos de la parte que actualiza el html. Prácticamente ésto nos permitirá ocuparnos solamente de la funcionalidad del componente.

 En muchos de los frames se facilitan instrucciones embebidas en las etiquetas, tipo como for o if, ésto no está contemplado en la publicación porque simplemente no he querido generar mucha complejidad. Para los que estén interesados en saber como se haría les diré que simplemente se trata de definir propiedades a las etiquetas y en los valores se toma el resto de instrucciones y los valores o argumentos. Claro que después hay que incluirlo, pero no es difícil. Ejemplo:

<etiqueta for="let i=0 to 10"></etiqueta>


Estructura principal e integración de los componentes

 Como vamos a tener todo organizado vamos a definir nuestro marco de trabajo, framework, con el nombre de app aunque podría ser cualquier nombre. Nuestro marco de trabajo incluirá una parte donde se indicarán los componentes, llamada igual, que existirán en el programa, instanciados en la página. Digamos que la parte del código es lo que representa o define el componente mientras que su instancia la llevará a cabo el motor del frame al examinar la página del programa.

 Por último la parte del main, es el ámbito donde trabajaremos con las estancias que estarán generadas como se dijo por el frame, invisible para nosotros. En dicha parte es donde definiremos el programa y el comportamiento de los componentes, por ejemplo definir un evento para que haga algo, como un click, o cualquier cosa.

 Ejemplo de la página:

<head>
    <meta>
    <style>
        etiqueta {
            background: red;
            display: block;
        }
    </style>
</head>

<body>
    <h1>Hola mundo</h1>
    <etiqueta></etiqueta>
    <etiqueta></etiqueta>
</body>

 El código en nuestro script:


app = {
    componentes: [{
        "etiqueta": {
            body: {
                campo1: 'soy el campo1',
                campo2: 'soy el campo2'
            },
            template: "<h1> {{ campo1 }} / {{ campo2 }} </h1>"
        }
    }],
    main() {
        this.etiqueta[0].campo1 = "Soy un frame sencillo sin mucha ambición";
        this.etiqueta[1].click = function() {
            alert("Hola holita");
        };
    }
}


Para no complicar la cosa, el motor genera las instancias en un array con el nombre de la etiqueta. Por ejemplo si agregáramos otro componente llamado listado y sólo hubiera una instancia en la página simplemente accederíamos a ella en el programa como listado[0]. En el caso que exponemos en el ejemplo observamos que tenemos un componente etiqueta definido en componentes y en la página tenemos dos instancias de este por eso accedemos con etiqueta[0] para el primero, orden descendente (luego veremos por qué), y al segundo componente accedemos por etiqueta[1].


Motor del framework


 El engine o motor es la parte que no se ve, es la que normalmente se llama por medio del enlace pertinente. Lo ideal sería crearse un código ofuscado del motor y vincularlo, así la página estaría más clara y sólo trabajaríamos con la estructura del frame.

 Para desarrollar este muy sencillo framework sólo vamos a requerir de una función recursiva que nos permitirá navegar por los nodos de forma amistosa. A la función le vamos a pasar el nombre de la etiqueta que queramos localizar y asignarle una acción.

// Herramienta
function buscaTag(nodo, tag, action, retorno) {
    tag = tag.toUpperCase();
    nodo.childNodes.forEach((item) => {
        if (tag == item.nodeName) {
            if (retorno) return action(item);
            action(item);
        }
        buscaTag(item, tag, action);
    });
}



 Luego habrá que inicializar el framework. En esta parte se comprueban los componentes disponibles y se instancian los componentes para que estén accesibles en el programa. En el código he usado la función eval(), para instanciar, pero puede hacerse de otra forma, eso es a gusto del consumidor.


// inicializadomos!!
buscaTag(document.getRootNode(), "html", function(item) {
    app.html = item;
}, true);
// Examinamos los componentes disponibles y creamos las instancias de estos
app.componentes.forEach(componente => {
    var i = 0;
    var nombreComponente = Object.keys(componente)[0];
    app[nombreComponente] = [];

    // Instanciamos componentes
    buscaTag(document.getRootNode(), nombreComponente, function(item) {
        item.id = nombreComponente + i;
        eval("app." + nombreComponente + "[" + i + "]=" + JSON.stringify(componente[nombreComponente].body));
        i++;
    }, false);
});
// Ejecutamos el programa
app.main();

 Y por último tenemos la parte que actualiza periódicamente. Como podrán observar las propiedades renderizables de las plantillas se buscan por medio de una sencilla expresión regular y luego se sustituye con el valor acorde que tenga el componente en ese momento. Aunque parezca todo muy estático podemos comprobar por ejemplo mediante la consola del navegador como es posible acceder a la aplicación por medio del objeto app y modificar la función click() de cualquiera de los dos componentes existentes en el ejemplo o cambiar el contenido de los campos que en un segundo estará todo actualizado.


// Actualizamos!! (cada segundo)
window.setInterval(() => {
    /////////////////////////////////////////////////////
    app.componentes.forEach(componente => {
        var nombreComponente = Object.keys(componente)[0],
            appComponente = componente[nombreComponente],
            i = 0;

        buscaTag(app.html, nombreComponente, function(item) {
            componente = app[nombreComponente][i];
            componente["render"] = appComponente.template;

            // sustituimos los atributos por sus valores
            Object.keys(appComponente.body).forEach(atributo => {
                componente.render = componente.render.replace(
                    new RegExp("({{)+[\\s](" + atributo + ")+[\\s]+(}})", "g"), componente[atributo]);
            });

            // introducimos el render
            let etiqueta = document.getElementById(nombreComponente + i);
            if (etiqueta.innerHTML !== componente.render) {
                etiqueta.innerHTML = componente.render;
            }

            // Añadimos propiedades especiales como eventos ...
            var elementoDom = document.getElementById(item.id);
            elementoDom.parentNode.replaceChild(item.cloneNode(true), item); // Eliminamos eventos
            if (app[nombreComponente][i].click !== undefined) {
                document.getElementById(item.id).addEventListener("click", app[nombreComponente][i].click);
            }

            i++;
        }, false);
    });
}, 1000);

 Como ven todo ésto de los frameworks no es magia y sólo se trata de una especie de patrón de diseño para organizar mejor el desarrollo y mantenimiento de la aplicación. Quizás en otra ocasión con tiempo pueda mostraros lo mismo pero en la parte del backend, en Php of course.

  Si quieren cacharrear subo el fichero a mi espacio Github para que piquen y hagan experimentos, que lo disfruten y me dicen si quieren que la próxima entrada sea el framework de Php o comenten alguna sugerencia. Bye.

No hay comentarios:

Publicar un comentario