Video Summary and Transcription
Caleb Porzio presenta Alpine JS, un framework de JavaScript, y demuestra el proceso de crear una versión rudimentaria de AlpineJS y refactorizarla. La charla cubre temas como la creación de un Dom Walker, la evaluación de expresiones, el uso de observadores de mutación y la refactorización del código. También se discuten técnicas como la inversión de condicionales, el uso de callbacks y el currying de parámetros. La charla enfatiza la importancia de la abstracción, la extracción de manejadores y un enfoque declarativo en el desarrollo de software.
1. Introducción a Alpine JS
Hola, mi nombre es Caleb Porzio. He creado un framework de JavaScript llamado Alpine JS. Vamos a crear una versión sencilla de AlpineJS, hacer un desastre y luego refactorizarlo utilizando técnicas de Alpine.
Hola, mi nombre es Caleb Porzio. He creado un framework de JavaScript llamado Alpine JS. Y cuando comencé con AlpineJS, no era un desarrollador de JavaScript experto, pero han pasado algunos años. He reescrito la base de código varias veces y ahora tengo muchas opiniones que creo que me ayudan a crear bases de código más mantenibles en las que realmente quiero trabajar.
Así que hoy vamos a crear esta pequeña versión sencilla de AlpineJS y básicamente vamos a hacer un desastre en el proceso, luego lo corregiremos y lo refactorizaremos utilizando algunas de las técnicas que he utilizado en Alpine. Así que vamos a hacerlo.
2. Creando el Dom Walker y Manejando Flame Click
Aquí hay un botón en una página con una etiqueta de script vacía. Vamos a crear un pequeño espacio de juego. En lugar de usar el clic de exón de Alpine, usaremos el clic de llama y escribiremos JavaScript dentro de él. Creemos un script de Dom Walker para buscar atributos de clic de llama e inicializarlos. Comprobaremos si un elemento tiene el atributo de clic de llama y obtendremos su contenido.
Aquí hay un botón en una página con una etiqueta de script vacía. Este será nuestro pequeño espacio de juego. Y el framework que vamos a escribir es, no sé si has visto Alpine, pero con Alpine, puedes agregar cosas como escuchadores de clic directamente a los botones agregando atributos directamente en el HTML, como clic de exón. Bueno, en lugar de eso, vamos a hacer clic de llama y luego podemos escribir cualquier JavaScript aquí dentro. Así que escribamos algo como muy caliente.
De acuerdo. Y ahora, si cargamos esto en el navegador, no va a pasar nada. Tenemos este botón con clic de alerta. Muy caliente, lo hacemos clic y no pasa nada porque no hemos escrito ningún JavaScript para él. Así que hagámoslo. Lo primero que haría es crear un pequeño Dom Walker, un pequeño script que recorrerá cada elemento de la página y nos dará la oportunidad de buscar cosas como clic de llama e inicializarlas realmente. Así que escribamos un Dom Walker. Y con la magia de los fragmentos, no tienes que sentarte aquí y verme escribir un pequeño Dom Walker. Aquí está el script del Walker. Y este es el punto donde tenemos esta pequeña variable llamada L. Que en cada iteración de este bucle while simplemente recorrerá el siguiente nodo. Así que solo imprimamos L en la consola, actualicemos la página y asegurémonos de que funcione. Si miramos la consola, aquí está. Tenemos el botón, haz clic en mí. Ahora tenemos tres botones en la página. Actualicemos, hagamos que sean tres. Y recorrerá los tres. Si tuviéramos un botón anidado dentro de un div, también recorrería ese div. Y luego el botón dentro de él. Bien, genial. Tenemos nuestro pequeño Dom Walker. Ahora en esta parte, podemos hacer cosas con cada elemento. Y en nuestro caso, queremos verificar la existencia de este atributo de clic de llama diremos si L tiene el atributo clic de llama, entonces dejemos que la expresión sea igual a L.getAttribute(clic de llama). Esto obtendrá el contenido de ese atributo. De acuerdo.
3. Evaluando Expresiones y Observadores de Mutación
Y esta expresión, simplemente registremos esta expresión, para que podamos ver que funcionó. Queremos escuchar realmente un evento de clic y luego evaluarlo. Ahora casi hemos terminado. Solo vamos a agregar una complejidad del mundo real a este lío. Eso será un observador de mutación. En Alpine, en realidad puedes cambiar o eliminar estos atributos sin recargar la página y Alpine detectará esos cambios. Así que escribamos un código de observador de mutación que nos permita engancharnos para eliminar ese atributo. ¿Qué queremos hacer cuando se haya eliminado el atributo? Bueno, queremos eliminar el escuchador de eventos que acabamos de agregar arriba. Así que hagámoslo. Ahora tenemos agregar escuchador de eventos.
Y esta expresión, simplemente registremos esta expresión, para que podamos ver que funcionó. Aquí tenemos alerta tan caliente. Y ahora, si queremos evaluar esto, hay esta pequeña función sucia. No se supone que debas usarla, pero la estamos usando para esta charla llamada eval, donde en realidad evaluará una cadena como código JavaScript.
Entonces, si actualizamos, evalúa alerta, tan caliente, pero no queremos eso. Queremos escuchar realmente un evento de clic y luego evaluarlo. Así que hagámoslo. L.addEventListener('click'). Y ahora podemos poner una pequeña función aquí. Hagámoslo. Bien, actualicemos. Y ahora no se mostrará la alerta hasta que haga clic, genial. Eso funcionó, ahora casi hemos terminado.
Solo vamos a agregar una complejidad del mundo real a este lío. Y eso será un observador de mutación. Así que en JavaScript, en tu navegador, hay una API llamada observador de mutación que puedes usar para observar mutaciones en el HTML. En Alpine, en realidad puedes cambiar o eliminar estos atributos sin recargar la página y Alpine detectará esos cambios. Si quitas un escuchador de clic en Alpine, en realidad eliminará el escuchador de clic del botón donde acabo de quitar el escuchador de clic en DevTools y ahora hago clic y en realidad no lo eliminó. Así que escribamos un código de observador de mutación que nos permita engancharnos para eliminar ese atributo. Hagámoslo. Bien. Nuevamente, no voy a hacerte ver cómo escribo todo, pero creamos un observador de mutación y especificamos algunas cosas como un filtro de atributos y que solo estamos observando mutaciones de atributos, y luego simplemente mostramos una alerta de eliminado desde aquí, solo para mostrarte que, okay, hacemos clic y funciona. Y ahora, si voy y realmente elimino este atributo, nos hemos enganchado en ese momento y podemos ejecutar código cuando se haya eliminado el atributo. ¿Qué queremos hacer cuando se haya eliminado el atributo? Bueno, queremos eliminar el escuchador de eventos que acabamos de agregar arriba. Así que hagámoslo. removeEventListener. Y cuando estás eliminando escuchadores de eventos, obtienes el elemento, especificas el nombre del evento y luego necesitas una referencia a la función controladora real que era el escuchador de eventos en primer lugar. Así que necesitamos extraer esto. Digamos que let handler = eval(expression). Bien, ahora tenemos agregar escuchador de eventos.
4. Refactorización y Diseño del Código
Y ahora podemos eliminar ese escuchador de eventos cuando se haya eliminado el atributo. Esto es todo lo que necesitamos para crear nuestro pequeño MVP, un pequeño framework de JavaScript. Así que comencemos a refactorizar y hablar sobre mis valores como programador. Lo primero que suelo hacer cuando voy a refactorizar una base de código es empezar desde el principio. Hay mucho ruido visual sobre el recorrido del árbol DOM que no es necesario para entender todo. Escribamos el código que queremos que exista: recorramos un elemento, como el cuerpo del documento, y un callback donde obtengamos el elemento actual mientras hacemos ese recorrido del DOM. Y esto es todo lo que necesitamos para leer el código.
Y ahora podemos eliminar ese escuchador de eventos cuando se haya eliminado el atributo. Muy bien. Haz clic tan caliente. Ahora, si entramos aquí y eliminamos este atributo, hacemos clic y ahora no podemos ver esa alerta porque se ha eliminado el escuchador de eventos.
Así que esto es genial. Esto es todo lo que necesitamos para crear nuestro pequeño MVP, un pequeño framework de JavaScript. Lo llamaremos hot JS. Sí, esto es un desastre. Este es todo el código que escribirías si solo quisieras hacer el trabajo. Así que el resto de esta charla será refactorizar este código a algo más sensato y mantenible. Porque en este momento, si quisiéramos agregar más directivas, como flame, click, sería como flame text o algo así. Tendríamos que duplicar todo. Y ahí, ya sabes, sería un completo desastre.
Así que comencemos a refactorizar y hablar sobre mis valores como programador. Estas son muchas de mis opiniones. Esta es como la guía de estilo de Caleb Horzio para escribir JavaScript, muchas de mis opiniones y cómo abordo la refactorización. Lo primero que suelo hacer cuando voy a refactorizar una base de código es empezar desde el principio. Porque muchas veces es intimidante. ¿Por dónde empiezas? Todo es un desastre. Y generalmente es como, simplemente recorro el código hasta encontrar la primera cosa imperfecta o la primera cosa que no me gusta y luego empiezo desde ahí. Así que si hacemos eso, no es difícil. De inmediato, digo, okay, hay mucho ruido visual sobre el recorrido del árbol DOM que no es necesario para entender todo. Podemos extraer eso fácilmente. Y tampoco me gusta que haya, uh, esta variable current aquí arriba y luego este elemento temporal, y luego hay más código de recorrido del árbol aquí abajo. Así que hay una extracción obvia que quiero hacer, y diseñemos por pensamiento deseoso. Esa es una pequeña cita de Adam Wathen, donde cuando vas a refactorizar, básicamente escribes el código que quieres que exista y luego lo haces realidad. Así que escribamos el código que queremos que exista. Sería genial simplemente decir recorramos un elemento, como el cuerpo del documento, la etiqueta body de esta página, y luego un callback donde obtengamos el elemento actual mientras hacemos ese recorrido del DOM. Y esto es todo lo que necesitamos para leer el código.
5. Extracting Code into a Function
Si estamos escribiendo este código para su consumo, esto es todo lo que necesitamos. Así que tengo un archivo utils.js donde vamos a almacenar todas estas funciones. Creemos una función llamada walk que acepte el elemento raíz y un callback. Podemos extraer el código que rodea al callback en la función walk. Esta es una extracción básica donde extraemos el código en una función y aceptamos un callback. Tenemos walk L y todo debería funcionar bien. Ejecutemos esto y asegurémonos de que funcione. No se pueden leer las propiedades de undefined porque no estamos pasando el elemento al callback. Todo funciona muy bien. Sigamos hasta encontrar algo que no sea perfecto. Esto es perfecto. Esto es perfecto. Aquí hay una gran declaración if, if L tiene atributo.
Si estamos escribiendo este código para su consumo, esto es todo lo que necesitamos. Todo este material son detalles de implementación.
De acuerdo. Así que tengo un archivo utils.js donde vamos a almacenar todas estas funciones. Creemos una función. La llamaremos walk y aceptaremos el elemento raíz y luego el caminante real, al que simplemente llamaremos callback. Y luego podemos extraer todo este código aquí. La mitad superior de pop aquí, ejecutamos ese callback, que será todo este código que pasaremos. Así que coloquemos esto aquí arriba. De acuerdo. Y luego este fragmento inferior, lo colocaremos aquí dentro. De acuerdo. Entonces, nuevamente, esta es una extracción bastante básica donde simplemente extraes el código que rodea algo en una función y aceptas un callback para el interior de lo que ese código estaba envolviendo. Así que tenemos walk L y todo lo demás debería funcionar bastante bien. Uh, esta es la única parte que necesitamos cambiar aquí. Y también necesitaremos importarlo en nuestro archivo aquí. Y ejecutemos esto y asegurémonos de que funcione. No funcionó. No se pueden leer las propiedades de undefined tiene atributo. Porque no estamos pasando el elemento al callback aquí. Digamos que ambos archivos se actualicen. Y ahí lo tenemos. Todo funciona muy bien. Muy bien. Comencemos desde el principio hasta encontrar algo que no sea perfecto. Esto es perfecto. Esto es perfecto. De acuerdo. Aquí hay una gran declaración if, if L tiene atributo.
6. Invertir Condiciones y Usar Cláusulas de Guardia
Cuando me encuentro con una gran condición, me gusta invertir la condición y retornar temprano usando cláusulas de guardia. Esto hace que el código sea más legible y evita la anidación excesiva.
Y cuando me encuentro con algo que no funciona, voy a Si L tiene atributo. Y cuando me encuentro con una declaración if como esta, hay algunas cosas que pasan por mi cabeza. En general, las condiciones añaden complejidad que necesitas almacenar en tu cabeza cuando estás leyendo tus propios programas, porque es como si todo este código solo funcionara cuando hay. Un atributo en el elemento llamado flame click. Así que necesito mantener ese conocimiento en mi cabeza de que todo este código, necesitamos estar dentro de esa condición, lo cual, ya sabes, a medida que lo lees, es como si se sumara a esa carga. Así que una pequeña cosa que me gusta, una pequeña técnica que me gusta, um, cuando tengo una, ya sabes, una gran condición es invertir la condición y retornar temprano. Así que en este caso, podemos decir si no L tiene atributo, entonces retornar. Y ahora podemos ejecutar básicamente el resto del código sabiendo que tenemos ese atributo, pero sin ese nivel adicional de indentación. Y en este escenario, no es la mejor refactorización, pero solo quiero mencionar que uso estas, se llaman cláusulas de guardia. Las uso todo el tiempo. Es como sacar los casos excepcionales en la parte superior para que puedas escribir el código en el camino feliz. Y si tienes como cuatro niveles de anidamiento y conviertes esos en cuatro cláusulas de guardia, eso es mucho más legible que varios, ya sabes, niveles de anidamiento en toda la función. Muy bien. Así que esa es una pequeña técnica.
7. Usar Callbacks y Evitar Valores Nulos
En lugar de usar una declaración if, utiliza un callback para recibir un valor. Crea una función llamada getAttribute para extraer la expresión. Evita posibles valores nulos utilizando un callback receptor. Esto asegura que la expresión exista y evita errores. Continúa con la implementación del código.
Otra técnica que me gusta es, en lugar de usar una declaración if, utilizar un callback para recibir un valor. Así que vamos a extraer esta expresión L.getAttribute en una pequeña función llamada getAttribute. Donde pasamos el elemento. Y el nombre del atributo que queremos obtener, que es flame click. Bien. Y escribamos esa función y hablaremos mientras avanzamos. getAttribute(elemento, nombre). Bien. Y algo como esto, vamos a poner esto aquí y llamaremos a esto nombre. Y ahora lo primero que podrías pensar en hacer es simplemente retornar esta expresión. Y así tendrías algo como let expresion igual a algo, pero entonces vuelves a donde empezaste. Tienes que decir, si no hay expresión, eso es un valor nulo, eso es un valor potencialmente nulo. Y cuando estás trabajando con código, como sabes, los valores que pueden ser potencialmente nulos son mortales para tus programas porque es muy difícil de determinar los pocos casos o muchos casos donde son nulos y luego obtienes todos esos errores, como tratar de, ya sabes, tratar nulo como una función o llamar a una propiedad en nulo o lo que sea. Así que lo que preferiría hacer en este escenario es crear un callback receptor. Así que en lugar de establecer una variable llamada expresión, ¿qué tal si creamos este pequeño callback receptor donde obtenemos expresión como el primer parámetro? Ahora, todo lo que está dentro de aquí tiene acceso a expresión, pero siempre estamos garantizados de que la expresión existe. Si no existe, este callback simplemente no se ejecutará. Así que convirtamos esa función en una función receptora. Así que si no tiene el nombre que estamos buscando, retornamos temprano, de lo contrario, agreguemos este pequeño parámetro aquí, este receptor, de lo contrario, en lugar de retornarlo, recibamos ese valor. Muy bien. Asegurémonos de que esto funcione. No va a funcionar porque no importamos esto. Ahora actualicemos y todo funciona. Genial. Muy bien. Sigamos adelante.
8. Abstracción y Extracción de Controladores
Creamos una función llamada cuando se elimina el atributo para manejar la abstracción del observador de mutaciones. Agregamos el atributo a la página y nos aseguramos de que funcione. El código ahora es más legible y ordenado. Extraemos la variable del controlador en una función separada llamada on.
Entonces tenemos nuestra función walk. Tenemos nuestro receptor getAttribute. Eso está bastante bien. Tenemos este addEventListener, y lo vamos a extraer en un minuto. Pero lo siguiente que veo realmente imperfecto es simplemente que hay una abstracción fácil aquí. Este observador de mutaciones, hay código arriba y hay código abajo. Así que se convierte en una abstracción de función realmente rápida. Así que hagámoslo. Vamos a crear una función llamada cuando se elimina el atributo. Y luego pasaremos el elemento, el nombre del atributo y luego un callback para ejecutar cuando se elimine ese atributo. Básicamente podemos tomar el código así, ejecutar el callback y luego el resto del código debajo de eso, podemos ponerlo debajo.
De acuerdo, perfecto. Ahora tenemos una función attribute is removed y podemos agregarla a la página aquí. Una función attribute is removed L y luego este flame click. De acuerdo. Y luego nuestro callback aquí. De acuerdo. Eso se ve bastante bien. Asegurémonos de que funcione. De acuerdo. Eso funciona. Y ahora si eliminamos este atributo, el clic ha sido eliminado. Muy bien. Esto ya es mucho más legible. Si solo lo miramos rápidamente, podemos ver que todo está mucho más ordenado. Hemos extraído muchas de estas funciones básicas, la última cosa imperfecta mientras avanzamos es que tenemos que establecer esta variable del controlador para poder agregarla como un event listener y luego eliminarla como un event listener más adelante. Y esto no es tan malo, pero imaginemos que hay como cien líneas de código entre aquí y aquí. Y simplemente no se siente bien tener esta variable temporal siendo referenciada en partes tan diferentes de la base de código y extraerla. Como si fuéramos a hacer una pequeña función, así que hagámoslo. Hagamos una pequeña función llamada on donde pasamos el elemento, el nombre del evento que deseas y luego el controlador, y hagámoslo.
9. Refactorizando el Event Listener
Entonces digamos on L click y luego handler, ya sabes, está bien. Pero este remove event listener, ¿cómo podemos hacer que esta parte sea parte de esta abstracción? Una técnica que me gusta usar es devolver la eliminación del event listener como una función desde el controlador on. Esto permite un código más limpio y evita la necesidad de una variable temporal separada. Importemos on y continuemos con el proceso de refactorización.
Entonces digamos on L click y luego handler, ya sabes, está bien. De acuerdo. Pero este remove event listener, ¿cómo podemos hacer que esta parte sea parte de esta abstracción, este on event listener y técnica que me gusta usar cuando estás tratando de extraer algo de dos capas diferentes de anidamiento. Hasta ahora, solo hemos extraído cosas en un solo nivel de condicionales o funciones o lo que sea. Solo un solo nivel de indentación. Esto es algo que queremos extraer tanto aquí como aquí. Así que se vuelve un poco complicado, no podemos simplemente copiar y pegar cosas aquí. Una técnica que me gusta usar para funciones que hacen algo como esto on, en realidad agrega un event listener. A menudo hay un desmontaje o algún tipo de procedimiento de limpieza para ese procedimiento. Como toda acción tiene una reacción opuesta. Como esta acción, este add event listener. También está la eliminación de ese event listener que está relacionada con agregarlo. Y para mantener eso en esta función, podemos devolverlo como una función. Mira esto. Si tomamos esto, eliminar event listener y realmente duplicamos esto y decimos, eliminar event listener así, ahora podemos devolverlo desde este controlador on, y luego no necesitamos tener este controlador como una variable temporal separada, podemos ver let off on L click de L expresión, y luego aquí abajo podemos simplemente ejecutar off. Esto es algo que me encanta, y hay tantas veces donde esto es útil, a menudo, ya sabes, cuando estás registrando event listeners o agregando a algún tipo de pila, es posible que desees devolver algún método o función para eliminarlo de esa pila o de la caché o es solo un patrón realmente útil que realmente me gusta, devolver un procedimiento de limpieza desde una función que tiene un procedimiento de limpieza. Entonces, de acuerdo, genial. Estoy realmente contento con esto. Importemos on y asegurémonos de que todo funcione. De acuerdo. Esto es como el paso uno en la refactorización. Creo que hemos llegado a un punto donde esto se ve bastante bien, pero es realmente solo una versión más legible de lo que comenzamos, es la misma estructura. Estamos recorriendo el DOM. Estamos obteniendo un atributo. Estamos escuchando un clic. Y luego, cuando se elimina el atributo, quitamos ese clic. Todavía hay muchos cambios estructurales que podemos hacer. Así que sigamos avanzando en eso.
10. Using Context and Parameter Currying
Las funciones get attribute y when attribute is removed tienen parámetros similares. Para reducir la duplicación y mejorar la legibilidad, podemos modificar get attribute para que devuelva una función llamada on remove. Esta nueva función aceptará un callback y llamará internamente a when attribute is removed utilizando el contexto de get attribute. Al utilizar el contexto, podemos crear una función que solo requiera el callback. Esta técnica, conocida como parameter currying, simplifica el código y hace uso del contexto. Alternativamente, podemos utilizar la función bind para lograr el mismo resultado.
De acuerdo. Entonces, lo primero que vamos a hacer es modificar get attribute y when attribute is removed, ya que básicamente tienen los mismos parámetros que se les envían. En este nivel de código, cuando estamos recorriendo el DOM y obteniendo atributos, siento que no necesitamos tener todo este conocimiento. No creo que necesitemos usar get attribute y when attribute is removed. Además, parece haber mucha duplicación. ¿Qué tal si en lugar de eso, get attribute nos devuelve una función llamada on remove que podemos utilizar en lugar de pasar todos estos parámetros? Creo que eso limpiaría mucho el código.
Entonces, hagamos eso dentro de get attribute. En lugar de solo recibir la expresión, vamos a recibir una función llamada on remove y simplemente aceptaremos un callback de esta manera. Luego, dentro de aquí, podemos llamar a when attribute is removed de esta forma. Aceptaría un callback y lo pasaríamos a when attribute is removed, ya que ya tenemos el elemento y el nombre. ¿Tiene sentido? Ya tenemos este contexto desde la llamada a get attribute, así que podemos crear una pequeña función que solo necesite el callback y llame a when attribute is removed. Ahora que tenemos on remove, podemos hacer esto. on remove. Y eso es mucho mejor. Podemos limpiarlo aún más y simplemente devolver esa función así. Creo que eso es mucho más limpio. Esta es una técnica que uso mucho, donde si tienes contexto, úsalo. En este caso, tenemos el contexto y lo estamos utilizando. Lo que estamos haciendo aquí se llama parameter currying, o algo así. No sé los términos técnicos, pero básicamente es cuando tomas una función y llenas previamente algunos parámetros para luego pasar esa nueva función que solo necesita un parámetro o lo que sea, no necesita todos los parámetros. Se llama currying. Hay varias formas de hacerlo en JavaScript. Esta es la más literal, pero podríamos haber hecho algo como esto. Let on remove = when attribute is removed.bind. Entonces puedes usar esta función de JavaScript llamada bind, donde pasas este contexto y luego llenas previamente los parámetros. En nuestro caso, L y nombre, y te devolverá una función que ya tiene estos parámetros llenos. Y luego puedes hacer algo como on remove. De acuerdo.
11. Refactorización hacia un Enfoque Declarativo
Este código es muy imperativo. Vamos a cambiarlo estructuralmente a un enfoque más declarativo registrando directivas y utilizando un método de inicio. Crearemos un nuevo archivo llamado directives.js para almacenar todas las funciones relacionadas con las directivas. Esto sigue el principio de un solo archivo. En lugar de clases, utilizamos módulos en JavaScript.
Entonces, creo que eso es una mejora y podríamos llegar incluso a pegarlo directamente en línea, porque va a devolver su función. Pero siento que eso es un poco demasiado. Siento que eso es un paso más allá de lo que necesitamos hacer. Muy bien. Y para nuestra gran refactorización final, nuevamente, todo esto es simplemente una forma más agradable de escribir lo que habíamos escrito antes. Cambiemos estructuralmente esto ahora mismo, este código es muy imperativo. Es decir, es paso a paso, es como recorrer el árbol DOM en cada iteración, verificar un atributo, obtener ese atributo, registrar un escuchador. En términos de mantenibilidad, eso no siempre es lo más mantenible. Entonces, si piensas en qué código quiero mantener, si este es mi pequeño marco de trabajo, ¿cuál es el código que si necesitara agregar un nuevo atributo, como flame text, tendría que copiar todo esto, tal vez esto se volvería enorme? Y todo está dentro de este walker. Es como, ¿y si simplemente fuera a un mundo de fantasía y escribiera lo que quiero? Bueno, en lugar de llamarlo attributes, llamémoslo directive. ¿Qué pasa si digo directive? Y luego podría decir algo como flame click, y luego tomemos todo este callback aquí. De acuerdo. E incluso, como estos son todos parámetros individuales que se pasan. Preferiría convertir esto en un objeto de cosas que se pasan para que pueda elegir qué cosas quiero, como, oh, solo quiero el elemento. O solo quiero el on remove, o solo quiero la expresión. Sí, esto está diseñado por pensamiento ilusorio. Para mí, esta es una forma mucho mejor de hacerlo. Entonces registramos la directiva, declaramos las directivas de manera más declarativa al principio, y ahora dentro de este walker tenemos algún método como boot o algo así como boot element. Y luego esta función de directiva registra una directiva en una pila de directivas, y luego boot recorre todas esas directivas y las inicia. Así que escribamos eso, vamos a salir de nuestros utils ahora mismo. Y vamos a crear un nuevo archivo llamado directives.js. Esto es otra cosa que me gusta hacer. Lo llamo el principio de un solo archivo, cuando estás escribiendo funciones que pertenecen juntas, las colocas en un solo archivo. Entonces, cualquier cosa relacionada con las directivas va en un solo archivo. De esa manera siempre sabes dónde encontrarlo. Si estás acostumbrado a la programación orientada a objetos, como PHP o Python o Java o algo así, estás acostumbrado a escribir clases como esta. Bueno, realmente no uso clases en JavaScript. Más bien uso estos módulos, estos archivos, donde en lugar de métodos, tengo funciones.
12. Exportación e Inicio de Directivas
Y si exporto la función, es una función pública. Si no lo hago, es solo una función privada utilizada dentro del archivo. Podemos agregar propiedades escribiendo variables en la parte superior. Exportemos la función 'call directive', donde pasamos el nombre de la directiva y un callback para la inicialización. Necesitamos almacenar las directivas en un array llamado 'directives' y agregar un objeto con el nombre y la función de inicialización. La función 'boot' iterará a través de las directivas y ejecutará el código para cada directiva, pasando el atributo, el elemento y la función de inicialización.
Y si exporto la función, es una función pública. Si no lo hago, es solo una función privada utilizada dentro del archivo. Y puedo agregar lo que equivale a propiedades simplemente escribiendo variables en la parte superior. Y entenderás a qué me refiero. Así que exportemos esta función 'call directive', donde pasamos el nombre de la directiva y luego un callback que maneja la inicialización. Podríamos llamarlo 'initialize' para que sea más legible o algo así.
De acuerdo. Y ahora aquí no tenemos los elementos actuales, así que tenemos que cambiar un poco la estructura. Necesitamos almacenar todas estas directivas para poder iniciarlas más tarde. Así que creemos una pequeña propiedad llamada 'directives' igual a. Y es solo un array. Y luego aquí diremos 'directives.push', y agreguemos un pequeño objeto con el nombre y luego esta función de inicialización. De acuerdo. Entonces, cuando se ejecute el código, cuando se ejecute esta directiva, simplemente las guardará para nosotros en esta variable, esta propiedad llamada 'directives' hasta que las usemos en 'boot'. Así que creemos esa función 'boot'. Muy bien. Y 'boot' acepta un elemento.
Y ahora 'boot' va a recorrer esas directivas. Entonces 'directives.forEach', y esto será una directiva, una individual. Y ahora esta directiva individual, podemos tomar este código que teníamos antes. Y ahora realmente lo ejecutaríamos así para esta directiva, obtendremos el atributo. Ya tenemos el elemento. Diremos 'directive.name'. Y en este caso, podríamos simplemente pasar 'directive.initialize', y tal vez hagamos eso solo por ahora para asegurarnos de que todo funcione, pero en realidad no vamos a hacer eso. En lugar de pasar 'initialize', vamos a pasar nuestro propio callback aquí. Y luego llamar a esta función de inicialización. Y recuerda, 'initialize' es lo que pasamos a la directiva y 'initialize' espera este objeto de parámetros. Así que simplemente peguemos eso aquí. De acuerdo. Tenemos el elemento, necesitamos la expresión.
13. Invertir el Flujo y Escribir Código Legible
Recuerda, hemos invertido el flujo elevando las declaraciones y usando una función de inicio para el bucle. Hay muchas más optimizaciones y refactorizaciones que se pueden hacer, pero por ahora, mantengámoslo a este nivel. Esta estructura es cómo se escribe Alpine, lo que lo hace más legible. Espero que hayas aprendido algo sobre la refactorización de JavaScript en funciones con nombres adecuados.
Recuerda, 'get attribute' devuelve un atributo y lo devuelve en 'remove'. De acuerdo. Así que importemos eso desde nuestro nuevo 'directives.js' e importemos 'boot'. Y ahora esto, necesitamos importar 'get attribute' desde 'utils'. Y veamos si esto funciona. 'Initialize' no está definido. De acuerdo. Así que tenemos 'initialize', pero en realidad es una propiedad de nuestra directiva, esta directiva que pasamos. Así que 'directive.initialize'. Y ahí lo tenemos. Todo sigue funcionando. Y para mí, hemos invertido un poco el flujo aquí. En lugar de recorrer directamente algo y hacer algo o hacer un puñado de cosas en cada bucle, hemos elevado esas cosas que estamos haciendo en declaraciones en la parte superior. Y ahora, cuando hacemos el bucle real, es solo esta pequeña función de inicio que recorre esas cosas que declaramos. Así que espero que esto tenga sentido para ti. Sé que, ya sabes, tener que mirar esto, no pasa fácilmente la prueba de la vista rápida. Pero sé que mirar esto puede ser un poco abrumador. Pero creo que entiendes la idea. Pero creo que entiendes la idea. Así que hay muchas más optimizaciones que podríamos hacer en este método. Podríamos proporcionar directamente aquí y completar parte de este contexto, como hicimos antes con el 'currying'. Hay tanto más que podríamos hacer. Hay tantas más refactorizaciones que me gustaría hacer, pero vamos a mantenerlo a este nivel por ahora. Y espero que esto te haya dado un poco de perspectiva sobre cómo me gusta escribir cosas y cómo me gusta estructurar el código y solo para que lo sepas, esto es muy similar a cómo se escribe Alpine en realidad. Hay un archivo en el núcleo de Alpine llamado 'directives' que tiene una función llamada 'directive' que almacena todos los controladores en este objeto y luego puedes inicializarlos más tarde. Todo lo que te he mostrado es básicamente cómo se escribe el núcleo de Alpine en sí mismo, lo que creo que lo hace mucho más legible y sí. Así que espero que hayas recogido uno o dos consejos sobre la refactorización de JavaScript en funciones con nombres adecuados. Sí. Gracias. Nos vemos.
Comments