Hace unos meses en la Polymer Summit 2017 desde el equipo Ionic lanzaban una pequeña bomba. Y es que Ionic 4 dejará de estar atado a Angular y podrá ser usado con la librería/framework que queramos. Es decir, que podremos usar Ionic con Angular (como venía siendo hasta ahora), con Vue.js, React o lo que queramos.

¿Por qué han tomado a esta decisión? Porque han visto que el camino que está siguiendo ahora mismo el universo Front es el de crear componentes reutilizables y agnósticos a cualquier framework. En este post nos centraremos en Stencil.js como una de las posibles soluciones.

A estas alturas, todos estaremos de acuerdo en que usar componentes para nuestra aplicación es una buena idea, ya que estos son más fáciles de mantener y se pueden reutilizar en cualquier parte.

Esto está muy bien, pero seguro que te ha pasado lo siguiente… ¿y si el componente ese tan chulo que me hice en Angular me hace falta en React (u otro framework)?

En ese caso no hay más remedio que intentar aprovechar el código que se pueda y hacer un port a otro framework. Otras veces, la única solución (aunque sea la menos atractiva) es volver a “picar” todo el Javascript.

Para evitar estos contratiempos, el equipo de Ionic ha aportado su solución creando Stencil.js, que no es más que un compilador de web components hecho con Javascript Vanilla.

Vale, pero… ¿qué son los web components?

Por si aún hay alguien que no sabe qué es un web component, tiremos de San MDN y veamos qué nos cuenta:

Los Web Components consisten en distintas tecnologías independientes. Puedes pensar en Web Components como en widgets de interfaz de usuario reusables que son creados usando tecnología Web nativa de cada navegador. Son parte del navegador, y por lo tanto no necesitan bibliotecas externas como jQuery o Dojo por ejemplo.

Un web component puede ser usado sin escribir código, simplemente añadiendo una sentencia para importarlo en una página HTML. Web Components usa capacidades estándar, nuevas o aún en desarrollo, del navegador.

Web Components hace uso de estas cuatro tecnologías (sin embargo cada una puede ser usada por separado):

Hasta aquí ha quedado más o menos claro, ¿no? Ahora vamos a entrar en detalle sobre qué aporta Stencil.js a lo que hemos visto hasta ahora y cómo funciona.

Un poco más claro, pero... ¿qué es Stencil.js?

Insistimos de nuevo: detrás de Stencil.js está el equipo de Ionic y éstos, según nos cuentan, se han inspirado en las mejores partes de Angular, React, Vue y Polymer.

Desde la propia web de Stencil.js se definen como: “El mágico, reusable compilador de web components”.

Veamos qué es y qué es lo que nos ofrece:

¿En qué navegadores se puede usar Stencil.js?

Primero saber que Stencil se encarga de añadir pollyfils a aquellos navegadores que no soporten Custom element, de esta forma Stencil será soportado por navegadores como Chrome, Safari, Firefox, Edge e IE11.

Veamos la extensa API de Stencil.js:

¡Ya está bien tanta palabrería, quiero ver código!

¡Manos a la obra! Haremos un componente abarcando toda la API de Stencil.js (que ya sabemos que no es mucha). Para ello hemos pensado en una modal, que pueda ser abierta desde otro componente o elemento del DOM.

Así que lo primero que debemos hacer es bajarnos e instalarnos el app-starter, que nos proporciona Stencil.js. Para ellos haremos lo siguiente desde nuestra consola:

git clone https://github.com/ionic-team/stencil-app-starter my-app

cd my-app

npm install

Una vez instalado, veamos qué tareas tienen preparadas para nosotros en npm:

Ahora echemos un vistazo a la estructura que nos ha creado Stencil:

Esta es la estructura que nos genera el starter-app de Stencil.js. Sólo vamos a prestar atención a la carpeta components, que es lo que nos interesa en este post.

Llegó el momento de ponernos manos al teclado. Creamos dentro de la carpeta “components” una carpeta my-modal, con los archivos: my-modal.tsx y my-modal.scss.

Ahora añadimos código a nuestro archivo my-modal.tsx:


import {Component, Prop, Element, Method, Event, EventEmitter, Listen, State} from '@stencil/core';
@Component({
 tag: 'my-modal',
 styleUrl: 'my-modal.scss'
})
export class MyModal {
}

En la primera línea importamos desde el core de Stencil.js lo que nos haga falta.

Nos fijaremos en el decorator @Component, donde se define el tag por el cual llamaremos a nuestro componente para usarlo en HTML.

También vemos un styleUrl, en el que indicamos la ruta a nuestro CSS o SCSS de nuestro componente.

Seguimos añadiendo código a nuestro my-modal.tsx. Ahora creamos las propiedades de nuestro componente dentro de nuestro class MyModal:


 @Prop() title: string = '';
 @State() isOpen: boolean = false;
 @Element() element: HTMLElement;
 @Event() open: EventEmitter<boolean>;
 @Event() close: EventEmitter<boolean>;

De aquí destacar la @Prop() title, que nos permitirá pasar por atributo del componente un string de la siguiente manera:


<my-modal title=”titulo de mi modal”></my-modal>

A continuación creamos dos métodos que expondremos al DOM mediante el decorador @Method. Con estos métodos abriremos y cerraremos la modal, emitiendo un evento para avisar del estado en el que se encuentra nuestra modal:


@Method() // Con este decorator, exponemos el método al DOM
 openModal(): void {
     // Emitimos un evento de modal abierto
     this.showModal(true);
     this.open.emit(true);
 }
 @Method()
 closeModal(): void {
     if (!this.isOpen) {
         this.showModal(false);
         // Emitimos un evento de modal cerrado
         this.close.emit(true);
     }
 }

Para finalizar crearemos un @Listen, que se encargará de escuchar el evento *keydown *de la tecla escape, para que cuando se pulse nos cierre la modal:


 // Escuchamos el evento del teclado keydown y más especificamente la tecla de "Escape"
 @Listen('window:keydown.escape')
 handleEscapeKey(): void {
     this.closeModal();
 }
 // Ciclo de vida, el componente se ha cargado pero aún no se ha renderizado.
 // Sólo se llamará una vez.
 // Es buen sitio para hacer actualizaciones de último momento antes de que se renderice.
 componentWillLoad() {
     this.showModal(this.isOpen);
 }
 showModal(show: boolean): void {
     this.isOpen = !show;
        this.element.classList.toggle('off', this.isOpen);
 }

Podemos observar un método componentWillLoad, éste es uno de los ciclos de vida que tiene un componente de Stencil.js.

Por último llegamos al render del componente.


render() {
   return (
         <div>
                <h1>{this.title}</h1>
             {/*Aqui iria todo el contenido que introduzcamos dentro del componente*/}
             <slot />
             <div class="modal-footer">
               <button
                    type="button"
                    class="btn-ok"
                 onClick={() => this.closeModal()}
               >
                 Aceptar
               </button>
             </div>
         </div>
     );
 }

De aquí destacaremos el tag ****, un tag especial de los web components. Este tag es un marcador de posición dentro del web component que se rellenará con su propio marcado, permitiendo crear árboles DOM por separado para luego presentarlos juntos.

Con todo esto nuestro archivo my-modal.tsx quedaría de la siguiente manera:


import {Component, Prop, Element, Method, Event, EventEmitter, Listen, State} from '@stencil/core';
@Component({
 tag: 'my-modal',
 styleUrl: 'my-modal.scss'
})
export class MyModal {
 @Prop() title: string = '';
 @State() isOpen: boolean = false;
 @Element() element: HTMLElement;
 @Event() open: EventEmitter<boolean>;
 @Event() close: EventEmitter<boolean>;
    @Method() // Con este decorator, exponemos el método al DOM
 openModal(): void {
     // Emitimos un evento de modal abierto
     this.showModal(true);
     this.open.emit(true);
 }
 @Method()
 closeModal(): void {
     if (!this.isOpen) {
         this.showModal(false);
         // Emitimos un evento de modal cerrado
         this.close.emit(true);
     }
 }
 // Escuchamos el evento del teclado keydown y más especificamente la tecla de "Escape"
 @Listen('window:keydown.escape')
 handleEscapeKey(): void {
     this.closeModal();
 }
 // Ciclo de vida, el componente se ha cargado pero aún no se ha renderizado.
 // Sólo se llamará una vez.
 // Es buen sitio para hacer actualizaciones de último momento antes de que se renderice.
 componentWillLoad() {
     this.showModal(this.isOpen);
 }
 showModal(show: boolean): void {
     this.isOpen = !show;
        this.element.classList.toggle('off', this.isOpen);
 }
 render() {
   return (
<div>
<h1>{this.title}</h1>
             {/*Aqui iria todo el contenido que introduzcamos dentro del componente*/}
             <slot />
<div class="modal-footer">
               <button type="button" class="btn-ok" onClick={() => this.closeModal()}
               >
                 Aceptar
               </button>
             </div>
         </div>
     );
 }
}

Ahora, para poder probar nuestro componente, dejaremos el index.html que nos generó Stencil.js de la siguiente manera:


<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
  <meta charset="utf-8">
  <title>Componente modal hecho con Stencil</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0">
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta http-equiv="x-ua-compatible" content="IE=Edge"/>
  <script src="/build/app.js"></script>
  <style>
 html {
   font-family: sans-serif;
 }
 .btn-container {
   width: auto;
   display: table;
   margin: 0 auto;
   position: relative;
 }
 .btn-container button {
   margin: 0 8px;
 }
 button {
   margin: 0;
   border: 0;
   border-radius: 5px;
   padding: 16px;
   display: inline-block;
   min-width: 175px;
   vertical-align: middle;
   white-space: normal;
   background: none;
   line-height: 1;
   outline: none;
   /* Browsers have different default form fonts */
   font-size: 13px;
   transition: background-color ease 0.3s;
 }
 button:hover {
   cursor: pointer;
 }
 button.btn-ok {
   background-color: --md-var-hashtag-209bc4;
   color: white;
 }
 button.btn-ko {
   background-color: --md-var-hashtag-e73131;
   color: white;
 }
 button.btn-big {
   width: 300px;
 }
  </style>
</head>
<body>
  <my-modal title="Mi titulo" id="modal">
 <h3>segundo título </h3>
 
   Este es un ejemplo de descripción para un modal hecho con Stencil.<br>
   Se puede añadir cualquier <strong>elemento</strong><br>
   Lorem ipsum dolor sit amet consectetur adipisicing elit. Cupiditate, libero blanditiis, perspiciatis odit alias
   error natus in dicta quos tempora a facere totam omnis animi iure incidunt illum odio
 
  </my-modal>
  <div class="btn-container">
 <button id="open-modal-button" class="btn-ok btn-big"> Abre la modal</button>
 <button id="close-modal-button" class="btn-ko btn-big"> Cierra la modal</button>
  </div>
  <script>
 (function(){
   var myModal = document.querySelector('my-modal');
      document.getElementById('open-modal-button').addEventListener('click', function () {
     myModal.openModal();
   });
      document.getElementById('close-modal-button').addEventListener('click', function () {
     myModal.closeModal();
   });
   myModal.addEventListener('close', function () {
     alert('Modal cerrado :)');
   });
 })();
  </script>
</body>
</html>

Como véis es realmente fácil hacer un componente con Stencil.js. La curva de aprendizaje es realmente baja y, al trastear un poco con la API, nos daremos cuenta de que podemos crear nuestros propios componentes más rápido de lo que creíamos.

Como cualquier tecnología que se precie tiene partes buenas y malas, de esta cabe destacar:

Pros:

Contras:

Conclusión

A pesar de que Stencil.js está en una fase muy temprana de desarrollo, se nota que detrás está el equipo de Ionic y que su objetivo es desarrollar un buen producto que pueda ser la solución para muchos proyectos.

Debemos tener en cuenta la evolución que están teniendo las tecnologías front y saber aprovechar cada una de ellas para lo que necesitamos. Cada vez nos encontramos con más y más frameworks que nos aseguran que, esta vez sí, tendremos la solución a todo lo que nos planteamos.

Nos encontramos con el dilema de qué tecnología escoger y qué futuro nos espera eligiéndola. Stencil, de alguna manera, viene a solucionar este problema.

Puede ser un buen complemento para trabajar junto a nuestros frameworks favoritos puesto que éste se integra fácilmente con cualquiera de ellos.

Stencil va a dar mucho que hablar, es una opción más que interesante y a tener en cuenta en nuestros próximos desarrollos.

Cuéntanos qué te parece.

Enviar.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.