Construcción y renderizado de su primer componente de joystick

Cómo construir una aplicación simple y escribir un componente usando @joystick.js/ui de CheatCode framework y renderícelo en el navegador usando @joystick.js/node .

Primeros pasos

Para este tutorial, vamos a utilizar el marco JavaScript de pila completa de CheatCode, Joystick. Joystick reúne un marco de interfaz de usuario de front-end con un back-end de Node.js para crear aplicaciones.

Para comenzar, querremos instalar Joystick a través de NPM. Asegúrese de estar usando Node.js 16+ antes de instalar para garantizar la compatibilidad (lea este tutorial primero si necesita aprender a instalar Node.js o ejecutar varias versiones en su computadora):

Terminal

npm i -g @joystick.js/cli

Esto instalará Joystick globalmente en su computadora. Una vez instalado, vamos a crear un nuevo proyecto:

Terminal

joystick create app

Después de unos segundos, verá un mensaje desconectado de cd en su nuevo proyecto y ejecute joystick start :

Terminal

cd app && joystick start

Después de esto, su aplicación debería estar ejecutándose y estamos listos para comenzar.

Creando el componente

Cuando creó su aplicación, si abre el package.json archivo en la raíz del proyecto, verá dos dependencias enumeradas:@joystick.js/ui y @joystick.js/node . Aunque estos son paquetes separados, están diseñados para trabajar juntos. Para que eso suceda, usamos el @joystick.js/cli paquete instalado arriba. Cuando ejecutamos joystick start arriba, se estableció esa conexión.

En el proyecto que creamos, verá una carpeta /ui en la raíz del proyecto con tres carpetas dentro:/ui/components , /ui/layouts y /ui/pages . Al crear componentes en Joystick usando el @joystick.js/ui paquete, usamos estos tres tipos para mantenernos organizados:

  • /ui/components contiene diversos componentes de Joystick que están destinados a ser renderizados junto con otros componentes o compuestos juntos en páginas.
  • /ui/layouts contiene componentes de Joystick que están destinados a ser envoltorios que representan contenido estático (por ejemplo, elementos de navegación o un pie de página) junto con una página dinámica.
  • /ui/pages contiene componentes de Joystick que representan páginas o URL en nuestra aplicación que pretenden ser composiciones de HTML y otros componentes asignados a una ruta.

Para este tutorial, nos vamos a centrar en el último tipo, páginas. La página que vamos a crear mostrará algunos elementos ficticios para demostrar todas las características de un componente Joystick.

Primero, creemos la carpeta y el archivo para el componente. Lo llamaremos tablero y lo almacenaremos en /ui/pages/dashboard/index.js :

/ui/pages/dashboard/index.js

import ui from '@joystick.js/ui';

const Dashboard = ui.component({
  render: () => {
    return `
      <div class="dashboard">
        <h4>Dashboard</h4>
      </div>
    `;
  },
});

export default Dashboard;

Para comenzar, queremos configurar un esqueleto para nuestro componente. Arriba, estamos importando el ui objeto exportado desde el @joystick.js/ui paquete que insinuamos anteriormente. Para configurar nuestro componente, creamos una nueva variable Dashboard y asignarlo a una llamada a ui.component() , pasando un objeto que contiene la definición de nuestro componente. En la parte inferior de nuestro archivo, nos aseguramos de exportar el Dashboard variable como predeterminada, ya que Joystick requiere que hagamos esto (veremos por qué en un momento).

Centrándose en el render propiedad que hemos establecido en el objeto pasado a ui.component() , esto se asigna a una función que es responsable de representar el marcado HTML para nuestro componente. En Joystick, los componentes se construyen con HTML puro. Cualquier HTML que escribirías en un .html simple El archivo funcionará en un componente Joystick.

En nuestro render() función, devolvemos una cadena, escrita con acentos graves `` para que podamos aprovechar la interpolación de cadenas de JavaScript (permitiéndonos incrustar valores dinámicos como variables o el resultado de llamar a una función dentro de nuestro HTML).

Dentro de esa cadena, escribimos el código HTML para nuestro componente; aquí, solo un <div></div> etiqueta con una clase y un <h4></h4> etiqueta dentro de eso para que podamos empezar. Aunque puede no parecer mucho, si renderizáramos esto ahora, veríamos nuestro <h4></h4> renderizado en pantalla.

Antes de hacer eso, desarrollemos un poco más nuestro HTML y agreguemos algo de CSS:

/ui/pages/dashboard/index.js

import ui from '@joystick.js/ui';

const Dashboard = ui.component({
  css: `
    .dashboard {
      width: 100%;
      max-width: 1000px;
      margin: 0 auto;
    }

    .dashboard h4 {
      margin-bottom: 20px;
    }

    .dashboard input {
      display: block;
      padding: 20px;
      font-size: 16px;
      border: 1px solid #ddd;
      margin-bottom: 20px;
    }

    .dashboard button {
      border: none;
      background: #000;
      color: #fff;
      font-size: 16px;
      padding: 20px;
      border-radius: 3px;
    }
  `,
  render: () => {
    return `
      <div class="dashboard">
        <h4>Dashboard</h4>
        <input type="text" />
        <button class="say-hello">Say Hello</button>
      </div>
    `;
  },
});

export default Dashboard;

Mismo componente, solo agregando algunas cosas. Abajo en el render() , hemos agregado un <input /> y un <button></button> (los pondremos en uso en un momento). La parte importante aquí es el nuevo css propiedad.

Nuevamente, usando `` acentos graves (además de la interpolación, esto nos permite hacer una cadena de varias líneas en JavaScript), hemos escrito algo de CSS para el marcado en nuestro render() función.

La idea aquí es que queremos aislar CSS por componente. Esto nos mantiene organizados, pero también evita colisiones de estilos cuando usamos un solo archivo CSS (o varios archivos CSS importados en un solo archivo).

Detrás de escena, cuando se renderiza nuestro componente, Joystick tomará este CSS y lo aplicará automáticamente a nuestro componente. Así evitamos problemas con la cascada en CSS creando estilos superpuestos o rotos. Los estilos se asignan directamente a su componente.

Además del alcance dinámico, Joystick también inyectará automáticamente este CSS en el <head></head> del HTML que representamos en el navegador, lo que significa que los estilos se representan automáticamente junto con el HTML de su componente. Centrándose en el CSS en sí, observe que estamos haciendo referencia a elementos y nombres de clases dentro del HTML de nuestro componente, sin necesidad de nada especial; Joystick manejará las cosas difíciles por nosotros.

/ui/pages/dashboard/index.js

import ui from '@joystick.js/ui';

const Dashboard = ui.component({
  state: {
    name: 'Friend',
  },
  methods: {
    sayHello: (component) => {
      window.alert(`Hello, ${component.state.name}!`);
    },
  },
  css: `
    ...
  `,
  render: ({ state }) => {
    return `
      <div class="dashboard">
        <h4>Dashboard</h4>
        <p>I'm going to say "Hello, ${state.name}!"</p>
        <input type="text" />
        <button class="say-hello">Say Hello</button>
      </div>
    `;
  },
});

export default Dashboard;

Avanzando, a continuación, para hacer que nuestro componente sea interactivo, agregaremos una función genérica a nuestro componente conocida como método. El methods propiedad aquí se le asigna un objeto con funciones de nombre personalizado que se pueden llamar desde cualquier otra parte del componente. A cada método que definimos se le pasa el component completo instancia como el último argumento disponible (por ejemplo, si llamamos a un método y le pasamos un valor, ese valor se convertiría en el primer argumento y component se convertiría en el segundo).

Aquí, estamos definiendo un método sayHello que queremos mostrar un cuadro de diálogo de alerta cuando se llama. En el interior, queremos que muestre un mensaje que diga "¡Hola, !" donde <name> es el valor actual del name propiedad en el state del componente objeto.

Dentro de un componente Joystick, state representa el visual actual estado del componente (piense en "estado de cosas visual"). Ese state pueden ser datos, configuraciones para parte de nuestra interfaz de usuario, cualquier cosa que desee. Para inicializar nuestro state valor (también conocido como establecer nuestro estado "predeterminado"), agregamos un state opción a nuestro componente, también pasó un objeto, con los nombres de los valores que queremos establecer en state cuando el componente se carga.

Para nuestro componente, queremos configurar name en state . Aquí, establecemos el valor predeterminado en 'Friend' . Así que está claro, si tuviéramos que llamar al sayHello funcionar como está, veríamos aparecer un cuadro de alerta que decía "¡Hola, amigo!" Conectemos eso ahora usando el lifecycle de nuestro componente métodos.

/ui/pages/dashboard/index.js

import ui from '@joystick.js/ui';

const Dashboard = ui.component({
  state: {
    name: 'Friend',
  },
  lifecycle: {
    onMount: (component) => {
      component.methods.sayHello();
    },
  },
  methods: {
    sayHello: (component) => {
      window.alert(`Hello, ${component.state.name}!`);
    },
  },
  css: `
    ...
  `,
  render: ({ state }) => {
    return `
      <div class="dashboard">
        <h4>Dashboard</h4>
        <p>I'm going to say "Hello, ${state.name}!"</p>
        <input type="text" />
        <button class="say-hello">Say Hello</button>
      </div>
    `;
  },
});

export default Dashboard;

Un componente de Joystick pasa por varias "etapas de vida" cuando lo renderizamos en el navegador, a lo que nos referimos como su ciclo de vida. Aquí, estamos agregando un objeto a nuestro componente lifecycle al que se le pueden asignar tres funciones:

  • onBeforeMount una función que se llama inmediatamente antes de que se represente un componente Joystick en el navegador.
  • onMount una función que se llama inmediatamente después de que un componente Joystick se representa en el navegador.
  • onBeforeUnmount una función que se llama inmediatamente antes de que se elimine un componente Joystick del navegador.

Para demostrar nuestro sayHello método, vamos a utilizar el onMount método/función del ciclo de vida (el nombre "método" es el término utilizado para describir una función definida en un objeto en JavaScript) para llamarlo. Todos lifecycle a los métodos se les pasa el component instancia, lo que significa que podemos acceder a nuestro methods a través de ese objeto. Dentro de nuestro onMount función, llamamos a component.methods.sayHello() para decir "cuando este componente se represente en la pantalla, muestre una ventana de alerta y salude al usuario".

Casi termino. Para terminar nuestro componente antes de pasar al enrutamiento, lo último que queremos hacer es conectar algunos controladores de eventos DOM.

/ui/pages/dashboard/index.js

import ui from '@joystick.js/ui';

const Dashboard = ui.component({
  state: { ... },
  lifecycle: { .. },
  methods: { ... },
  css: `
    ...
  `,
  events: {
    'keyup input': (event, component) => {
      component.setState({ name: event.target.value });
    },
    'click .say-hello': (event, component) => {
      component.methods.sayHello();
    },
  },
  render: ({ state }) => {
    return `
      <div class="dashboard">
        <h4>Dashboard</h4>
        <p>I'm going to say "Hello, ${state.name}!"</p>
        <input type="text" />
        <button class="say-hello">Say Hello</button>
      </div>
    `;
  },
});

export default Dashboard;

Primero, centrémonos en el events propiedad que hemos agregado a nuestro componente. Así es como definimos y asignamos automáticamente el alcance de los detectores de eventos DOM a nuestro componente. Los oyentes se definen estableciendo una función de devolución de llamada en una propiedad cuyo nombre es una cadena con algún tipo de evento DOM, seguido de un espacio, seguido del selector DOM para adjuntar el evento.

Aquí, estamos agregando dos detectores de eventos:primero, un keyup oyente en nuestro <input /> y segundo un click oyente en nuestro <button></button> usando su nombre de clase say-hello . Para nuestro evento keyup, queremos actualizar dinámicamente nuestro state.name valor a medida que escribimos en la entrada. Para hacerlo, asignamos dos argumentos a nuestra función, event que representa el evento keyup del DOM y component (nuestra instancia de componente) como la segunda.

En el component ejemplo, un .setState() se define el método que toma un objeto que contiene las propiedades que queremos establecer (o sobrescribir) en el estado. En este caso, queremos sobrescribir name , ajustándolo al valor actual de nuestra entrada. Aquí, usamos el JavaScript simple event.target.value propiedad para acceder a ese valor donde event.target es igual al elemento HTML que activa el evento y value siendo el valor actual de ese objetivo.

Abajo en nuestro click controlador de eventos, usamos la misma estructura de argumentos, esta vez omitiendo el uso de event y accediendo a nuestro sayHello() método a través del component.methods objeto en nuestra instancia. La idea aquí es que cada vez que hacemos clic en nuestro botón, nuestro window.alert() en sayHello() se activará, mostrando el valor más reciente (suponiendo que hayamos escrito algo en nuestra entrada, esperaríamos ver eso).

Antes de continuar, queremos mencionar un cambio menor en nuestro render() HTML de la función. Observe que hemos agregado un <p></p> que incrusta el valor actual de state.name usando una expresión de interpolación de JavaScript ${state.name} . Notarás que hemos usado la desestructuración de JavaScript en el render() función, "arrancando" el state valor de ese objeto. Ese objeto es nuestra instancia de componente. Aquí, usamos la desestructuración para eliminar la necesidad de escribir component.state y en su lugar, simplemente arranca state directamente.

Eso es todo para nuestra definición de componente. A continuación, saltemos al servidor y conectemos una ruta para que podamos verla en el navegador.

Definición de una ruta y uso de res.render() para representar el componente

Una ruta es el nombre técnico de una URL que representa algo en nuestra aplicación. Para definir una ruta, debemos movernos al código que se ejecuta en el lado del servidor de nuestra aplicación en el index.server.js archivo en la raíz de nuestro proyecto.

/index.servidor.js

import node from "@joystick.js/node";
import api from "./api";

node.app({
  api,
  routes: {
    "/dashboard": (req, res) => {
      res.render("ui/pages/dashboard/index.js");
    },
    "/": (req, res) => {
      res.render("ui/pages/index/index.js", {
        layout: "ui/layouts/app/index.js",
      });
    },
    "*": (req, res) => {
      res.render("ui/pages/error/index.js", {
        layout: "ui/layouts/app/index.js",
        props: {
          statusCode: 404,
        },
      });
    },
  },
});

En una aplicación Joystick, la contraparte del lado del servidor de @joystick.js/ui es @joystick.js/node . Este paquete es responsable de configurar nuestro backend, específicamente, activar una instancia de Express.js y ejecutar un servidor HTTP para nuestra aplicación (de manera predeterminada, esto se inicia en el puerto 2600 pero se puede personalizar si lo deseamos). De ese paquete, se exporta un objeto que hemos importado en el código anterior como node . En ese objeto, tenemos una función .app() que es responsable de configurar nuestro back-end.

Cuando lo llamamos, le pasamos algunas opciones diferentes, la que nos interesa para este tutorial es routes que se establece en un objeto de rutas que queremos definir en nuestra aplicación. Arriba, tenemos dos rutas predefinidas (estas son incluidas automáticamente por joystick create vía @joystick.js/cli ):/ y * , una ruta de índice y un catch-all, ruta 404 * .

El que nos importa aquí es el /dashboard ruta que hemos agregado (hemos elegido este nombre porque coincide con el nombre de la página que definimos pero podríamos llamarlo /pizza si quisiéramos).

Una ruta definida en el routes objeto no es más que una ruta Express.js (por ejemplo, app.get() ). La diferencia aquí es puramente sintáctica y de organización. Definimos todas nuestras rutas juntas para mayor claridad y para mantener nuestro código consistente. Al igual que con una ruta Express.js normal, tenemos una función de devolución de llamada que se llama cuando se visita nuestra ruta (conocida como una "coincidencia" para la URL en el navegador).

Dentro de nuestra devolución de llamada aquí, llamamos a una función especial definida por Joystick en Express res objeto ponse, res.render() , pasando la ruta a la página que queremos renderizar (Joystick requiere que pasemos la ruta completa, incluido el .js extensión). Detrás de escena, Joystick hará algunas cosas automáticamente:

  • Represente nuestro componente como HTML (conocido como SSR o representación del lado del servidor) para enviarlo como respuesta inicial al navegador.
  • Encuentre el archivo JS correspondiente que ha sido compilado (es decir, código seguro para el navegador) por @joystick.js/cli e incrustarlo en el HTML de SSR.
  • En development , Joystick también incluye algunas funciones de utilidad y el script HMR (recarga de módulo en caliente) para actualizar automáticamente el navegador cuando cambiamos nuestro código.
  • Ubica todo el CSS en nuestro árbol de componentes (solo tenemos un nivel en nuestro árbol, pero si anidamos componentes, estos también se escanearían) y lo incrusta en el <head></head> etiqueta de nuestro HTML.

Con todo esto hecho, el HTML resultante se devuelve al navegador y se presenta para nuestro usuario. Dentro del archivo JavaScript seguro del navegador para nuestro componente de página, Joystick incluye automáticamente el script necesario para "montar" nuestro componente en el navegador.

Este es un proceso conocido como hidratación. Inicialmente enviamos algunos secos , HTML renderizado del lado del servidor para la solicitud inicial y luego cargue algo de JavaScript en el navegador para hidratar ese HTML seco haciéndolo interactivo nuevamente (es decir, cargando las partes dinámicas de nuestro JavaScript en el navegador).

Eso es todo. Si abrimos nuestro navegador y nos dirigimos a http://localhost:2600/dashboard , deberíamos ver nuestro cuadro de diálogo de alerta y después de hacer clic en Aceptar, ver nuestro componente. Intenta escribir tu nombre en el cuadro y hacer clic en el botón "Di hola" para verlo en acción.

Terminando

En este tutorial, aprendimos cómo instalar Joystick CLI (@joystick.js/cli ), cree una nueva aplicación y cree un componente Joystick usando @joystick.js/ui . Aprendimos sobre las diferentes características de un componente como estado, CSS, eventos DOM y métodos, así como también cómo definir una ruta y representar ese componente a través del res.render() método en el servidor.