Jet frontend. La historia de cómo reescribimos todo de nuevo.

Hola, soy Katya de Yandex. Dinero de nuevo. Continúo mi historia sobre cómo dejé de inventarme y comencé a vivir. En la primera parte, conté cómo llegué aquí y qué hacen nuestros desarrolladores front-end. Hoy, sobre la pila frontal, de dónde es React y dónde se fue BEM.

Spoiler: BEM no ha ido a ninguna parte ¯ \ _ (ツ) _ / ¯. Vamos!



Atención: alta concentración de frontend. Mucho texto, imágenes y código, como se prometió.

Parte 2. Sobre tecnología


Lejos 2016. Intentando escribir en React, resulta bastante tolerable. Todavía no sospecho que en un año transferiré servicios completos a React. 2017 comienza en Yandex. Dinero, tengo un BEM del cerebro, y todavía no sospecho.

Backend en Node.js, mi primera vez


Para familiarizarse con el proyecto, un nuevo desarrollador recibe una tarea de prueba. Tuve suerte: tuve esta tarea de la cartera de pedidos. Y el primer día me encontré con Node.js.

El front-end en Yandex.Money es responsable no solo del lado del cliente, sino también de la capa del servidor como una aplicación Node.js. La tarea de la aplicación es organizar los datos del backend de Java para la preparación en una forma orientada a la vista, así como el enrutamiento y la representación del servidor. Te lo habrían dicho hace un par de años, no habría entendido nada, y todo es bastante simple: cuando una solicitud llega desde el navegador al servidor, Node.js genera solicitudes HTTP para el backend, recibe los datos necesarios y las páginas web de plantillas. Usamos Express como el marco del servidor, y para desarrollar aplicaciones internas sin un enlace heredado , decidimos usar Koa2 . A los desarrolladores les encantó el diseño del marco, y decidimos no degradar a Express, por lo que Koa2 permaneció en la pila. Pero no implementamos el código Koa2 para usuarios externos: el marco no tiene suficiente soporte, pero hay vulnerabilidades abiertas.

Ya escribimos sobre el lugar de Node.js en nuestra interfaz, pero desde entonces algo ha cambiado. Node.js 8 se ha convertido en LTS y ya se está ejecutando en nuestros servidores de producción. También queremos abandonar los servidores Nginx, que generamos en cada host para distribuir estadísticas; serán reemplazados por servidores separados con Nginx y algún día CDN.

Para buscar código entre proyectos, pero no para que esté disponible públicamente, utilizamos un conjunto completo de herramientas: almacenar los módulos en Bitbucket y recopilarlos en Jenkins. También utilizamos el registro local de paquetes y, gracias a esto, no vamos a la red externa, esto acelera el ensamblaje y aumenta la seguridad de todo el sistema. Los javists nos sugirieron este enfoque, son geniales. Ama a tus seguidores;)

También realizamos un experimento: introdujimos un administrador de procesos en una de las aplicaciones, lo que simplificó la administración de servicios en Node.js. Ayudó con la agrupación y también nos salvó de un antiguo script de bash que ejecutaba aplicaciones.

Y toda la pila no es suficiente


Tenemos javascript en todas partes en la interfaz. Y en el servidor, y en el cliente, y bajo el capó de las herramientas internas. Conocemos otros idiomas, pero JavaScript hace un gran trabajo.

Pero BEM en el marco de nuestras tareas no hace frente a todo.

¿Qué es el BEM?
BEM es un enfoque de desarrollo web inventado por Yandex durante la vida útil de las páginas HTML estáticas y las cascadas CSS. Todavía no había un enfoque de componentes, y era necesario mantener la uniformidad de muchos servicios. Yandex no se sorprendió y desarrolló su propio enfoque de componentes, que hoy le permite crear componentes aislados y escribir código declarativo flexible.

BEM no es solo una metodología, sino también un gran conjunto de tecnologías y bibliotecas. Algunos de ellos se adaptan a los detalles específicos de BEM, y algunos bien pueden usarse de forma aislada de la arquitectura BEM. Si necesita un poderoso motor de plantillas o un ejemplo digno de abstracción de componentes sobre el DOM en su proyecto, ya sabe dónde encontrarlos;)

Por lo tanto, comenzamos a transferir servicios a React. Algunos de ellos ya viven en dos aplicaciones creadas en diferentes pilas:

- una plataforma específica para Yandex BEM;
- Reacciona el ecosistema joven y de moda.

Yandex Technologies


Es hora de decirte por qué me enamoré de BEM.

Niveles de redefinición


Niveles, niveles, niveles ... ¡BEM! Beneficio!
Los niveles superiores son una de las principales características de la metodología BEM. Para entender cómo funcionan, mira la imagen:


La imagen está formada por capas superpuestas. Cada capa cambia la imagen final, pero no cambia las otras capas. La capa se puede extraer o agregar fácilmente, y la imagen cambiará.
Los niveles de anulación hacen lo mismo con el código:


El comportamiento del componente se forma durante el ensamblaje del código. Para agregar un comportamiento adicional, simplemente conecte el nivel deseado al ensamblaje. El código del módulo desde diferentes niveles como si estuvieran superpuestos. En este caso, el código fuente no cambia, pero tenemos un comportamiento diferente, combinando diferentes niveles.

Cuales son los niveles
La imagen de arriba muestra varios niveles de redefinición:
  • El nivel básico, la biblioteca, proporciona el módulo de código fuente;
  • El siguiente nivel, el proyecto, modifica este módulo a las necesidades del proyecto;
  • Una plataforma de nivel superior hace que el mismo módulo sea específico para diferentes dispositivos;
  • La guinda del pastel, el nivel de los experimentos, cambia el módulo para las pruebas A / B.


El nivel del proyecto es independiente del nivel de la biblioteca, por lo que la biblioteca es fácil de actualizar. El nivel de plataforma le permite usar un ensamblaje diferente para diferentes dispositivos. Y el nivel con el experimento está conectado para probar en los usuarios y también se apaga fácilmente cuando se obtienen los resultados.

El desarrollador mismo decide qué niveles necesita: puede crear un nivel con un tema o un nivel con el mismo código en un marco diferente.

Los niveles le permiten escribir módulos complejos basados ​​en simples, combinar fácilmente el comportamiento y buscar el mismo código entre servicios. Y este código es recopilado por ENB - Webpack en el mundo BEM.

Cuando me familiaricé con BEM, quedé especialmente satisfecho con las bibliotecas de interfaz de usuario en las que se encuentran los componentes listos para usar. Estamos ampliando estos componentes dentro del marco de nuevas bibliotecas y compartiéndolos entre proyectos. Esto hace la vida más fácil: rara vez invento, no escribo el mismo tipo de JS y ensamblo rápidamente interfaces a partir de bloques prefabricados.



Ahora echaremos un vistazo más de cerca a las herramientas de la plataforma BEM para comprender qué BEM no funciona lo suficientemente bien y por qué no se ajusta a nuestras tareas.

BEM-XJST


Comenzaré con mi motor de plantillas bem-xjst favorito. Antes de Yandex.Money, usé Jade, y Bem-xjst ilustró perfectamente las desventajas de Jade, que no vi entonces. Las plantillas bem-xjst son declarativas [1], no tienen un infierno [2], y cumplen perfectamente los requisitos del enfoque de componentes [3]. Todo esto se ve claramente en el ejemplo:



En el sandbox, puedes ver el resultado de la plantilla y jugar con ella.

Como funciona Dentro está el secreto de la arquitectura perfecta;)
  • escribe BEMJSON. BEMJSON es un JSON que describe un árbol BEM. Un árbol BEM es una representación del árbol DOM como componentes independientes;
  • bem-xjst acepta BEMJSON como entrada y aplica patrones. Este proceso se puede comparar con la representación en un navegador. El navegador omite el árbol DOM y aplica gradualmente reglas CSS a sus nodos DOM: tamaño, color del texto, sangría. Bem-xjst también pasa por alto BEMJSON, busca plantillas correspondientes a sus nodos y las aplica gradualmente: etiqueta, atributos, contenido. "Aplicar una plantilla" significa generar una cadena HTML a partir de ella. La generación de HTML de BEMJSON es manejada por uno de los motores de plantillas: BEMHTML.


Escribir plantillas es simple: seleccione la entidad y escriba las funciones que llamará el motor de plantillas para representar partes de la cadena HTML. Lo más difícil es resaltar la esencia. ¡Las entidades correctas son la clave de una buena arquitectura!

Cuanto más larga sea tu barba, mayores serán las posibilidades de que ya hayas notado una referencia en el nombre de la plantilla: XSLT (Transformaciones de lenguaje de hoja de estilo extensible) => XJST (Transformaciones de JavaScript extensible). Utiliza los principios de XSLT y, por lo tanto, es muy declarativo. Si no sabes qué es XSLT, considérate afortunado :)

Bem-xjst es isomorfo. Representamos páginas HTML en el servidor y lo cambiamos dinámicamente en el cliente. Para crear plantillas en tiempo de ejecución, bem-xjst proporciona una API que usamos al escribir código javascript del lado del cliente.

I-bem


Con bem-xjst describimos la vista y la lógica con i-bem . I-bem es una abstracción sobre el DOM que proporciona una API de alto nivel para trabajar con componentes. En pocas palabras, escribamos esto:



en cambio:



Para escribir código, no necesita saber acerca de la implementación interna del componente. Operamos con entidades que describimos en la plantilla: de todos modos, será un selector jQuery o un elemento DOM. Podemos crear eventos personalizados adecuados para un modelo de objeto en particular, y trabajar con eventos nativos y las interfaces se ocultarán en la implementación interna. La lógica de bajo nivel también se describe allí, lo que significa que no cargamos el código con la lógica principal con comprobaciones innecesarias. Como resultado, el código es fácil de leer y no depende de una tecnología específica.

I-bem le permite describir la lógica del componente como un conjunto de estados [1]. Este es javascript declarativo. I-bem implementa su propio Emisor de eventos: cuando los estados cambian, los componentes generan automáticamente eventos a los que otro componente puede suscribirse [2].

Así es como se ve la mayoría del código JavaScript del lado del cliente BEM:



Como funciona
  • mediante el evento domReady i-bem, encuentra componentes (bloques) en el árbol DOM y los inicializa: crea un objeto js en la memoria del navegador que corresponde al bloque;
  • Tras la ocurrencia de los eventos necesarios, establecemos los marcadores de bloque que reflejan el estado. El papel de los marcadores lo realizan las clases CSS. Por ejemplo, cuando hacemos clic en input, le agregamos la clase "input_focused", que sirve como marcador;
  • Al configurar dichos marcadores, i-bem inicia las devoluciones de llamada especificadas en la implementación de JavaScript del bloque.

La lógica de escritura es simple: debe describir los posibles estados del bloque (los mismos marcadores) y establecer controladores para cambiar estos estados (las mismas devoluciones de llamada).

Con i-bem, podemos redefinir fácilmente el comportamiento de los componentes, crear una API bien formada para su interacción y cambiarlos dinámicamente en tiempo de ejecución. Entonces, ¿qué falta?
Nos encanta BEM por su capacidad de declaración, escalabilidad fácil y abstracciones de alto nivel, pero ya no estamos listos para soportar sus limitaciones. A continuación, consideraremos el problema de la representación del cliente, el almacenamiento de datos y otras limitaciones de la plataforma BEM. Con el tiempo, los contribuyentes de BEM pueden resolver estos problemas, pero no estamos listos para esperar.

La web moderna con SPA y la adaptabilidad para dispositivos móviles también requiere adaptabilidad de nuestra parte. Por lo tanto, decidimos cambiar a nuestra propia pila. Y eligieron Reaccionar.

Nueva pila de arce React


React trajo a nuestras vidas un DOM virtual, recarga en caliente, CSS en JS y una gran comunidad de la que nos hemos convertido en parte.

La migración de nuestros servicios a React está en pleno apogeo, algunas aplicaciones ya se han reescrito total o parcialmente en React. Conocemos nuevos enfoques y herramientas y mejoramos la arquitectura de nuestras aplicaciones.

Bibliotecas


Particionar entidades de interfaz en bloques BEM independientes es muy amigable con el enfoque del componente React. Los desarrolladores de Yandex escribieron bem-react-core y transfirieron la biblioteca de interfaz de usuario del componente base a React. Escribimos una biblioteca de adaptadores que tiene en cuenta los detalles de estos componentes y los suministra como HOC :



Dichas bibliotecas están conectadas no en la aplicación, sino en la biblioteca principal del componente:



La aplicación depende solo de la biblioteca principal y obtiene todos los componentes de ella:



Esto reduce el número de dependencias de la aplicación, y las bibliotecas no entran en el paquete dos veces en diferentes versiones.

Tecnología


React no está vinculado a tecnologías específicas y elegimos herramientas y bibliotecas nosotros mismos. Hay axios, redux, forma redux, redux thunk, componentes con estilo, TypeScript, flow, broma y otras cosas interesantes en mi armamento. Para evitar el zoológico, coordinamos el uso de nuevas tecnologías con otros desarrolladores: enviamos una solicitud de extracción a un repositorio especial con un análisis de cuán útil es la tecnología y por qué se eligió.

El front-end entra al bar, y el cantinero le dice


Para las aplicaciones en React, estamos creando una plataforma que reunirá bibliotecas y procesos para crearlos y respaldarlos. El corazón de esta plataforma es la utilidad de consola Frontend Bar. Bar puede cocinar muchas piezas deliciosas.

En el menú:

  1. Configuración con hielo: la barra mezcla y agita sus variables yml y prepara una plantilla de configuración para ansible.
  2. Jugo con el aroma de los configuradores: bar creará una nueva aplicación basada en un blanco modular: Juice.
  3. Conjunto de configuraciones básicas de la biblioteca. Próximamente

Crear una aplicación jugosa ahora es fácil: la barra frontal hace jugo. ¡Haga jugo, no guerra! Cuando Bar implementa una nueva aplicación, ejecuta un conjunto de configuraciones desde Juice: package.json, se genera .babelrc, middleware clave y código de rutas, código de componente raíz. Frontend Bar facilitará la asignación de nuevos microservicios y ayudará a cumplir con las reglas uniformes para escribir código.

Al pasar a una nueva pila, comenzamos a mejorar la arquitectura de las aplicaciones del servidor: escribimos un nuevo registrador para el cliente y una biblioteca con un conjunto de abstracciones para implementar MVC . Hoy decidimos cuál será la nueva arquitectura del servidor.



Spoiler: elige cebolla.

¿Qué pasó y mejoró? Entendamos


Interfaces dinámicas


Era


Escribí anteriormente que bem-xjst proporciona una API para crear plantillas en tiempo de ejecución. I-bem, a su vez, puede funcionar con el árbol DOM. Los haremos amigos y podremos generar y modificar dinámicamente HTML. Intentemos cambiar el botón por evento:




En este ejemplo, el lado débil de BEM es visible: i-bem no quiere ser amigo de bem-xjst y no quiere saber nada sobre plantillas. Agrega una clase al bloque, pero no aplica la plantilla [1]. Vuelve a renderizar el componente manualmente [2]:

  • describe una nueva pieza del árbol BEM [3];
  • luego aplique una nueva plantilla [4];
  • e inicializar otro componente en el nodo DOM actual [5].

Además, i-bem no crea diff de árboles BEM, por lo tanto, se representa todo el componente, no las partes que han cambiado. Considere un ejemplo simple: vuelva a representar el contenido de una ventana modal a pedido. Se compone de tres elementos:



Por simplicidad, asumimos que solo un elemento puede cambiar.



Quiero hacer [1] y relajarme. Pero i-bem no entenderá lo que ha cambiado, volverá a procesar completamente el componente completo y también se relajará. En este ejemplo, no habrá consecuencias graves, pero ¿qué pasa si los formularios completos se presentan de manera incorrecta? Esto empeora el rendimiento y causa efectos secundarios desagradables: en algún lugar la entrada parpadea, la información sobre herramientas sin propietario se cuelga en algún lugar. Debido a esto, estamos tristes y controlamos manualmente partes de los componentes para crear un renderizador de puntos [2]. Esto complica el desarrollo, y nuevamente estamos tristes.

Se ha convertido


La reacción vino y arruinó todo. Él mismo monitorea el estado de los componentes, ya no gestionamos el renderizado manual y no pensamos en interactuar con el DOM. React contiene una implementación virtual de DOM . Llamar a React.createElement crea un objeto js del nodo DOM con sus propiedades y descendientes: el DOM virtual de este componente, que se almacena dentro de React. Cuando un componente cambia, React calcula el nuevo DOM virtual y luego la diferencia entre el guardado y el nuevo, y actualiza solo la parte del DOM que ha cambiado. Todo vuela, y solo podemos optimizar la lógica compleja usando shouldComponentUpdate. Es un exito!

Almacenamiento de datos


Era


En BEM, preparamos todos los datos en el servidor y los transferimos a los componentes de la página:



Los componentes están aislados y no compartirán datos entre sí, lo que significa que los mismos datos deberán arrojarse a diferentes componentes [1]. No podremos obtenerlos en el cliente, por lo que cada componente acepta de antemano un conjunto de datos que se necesitan para todos los escenarios posibles de su operación. Esto significa que cargamos el componente con datos que tal vez no necesite [2].

A veces una entidad global nos rescata, en la que se almacena parte de los datos comunes, pero el almacenamiento global de variables no encaja bien con el concepto BEM. Por lo tanto, escribimos un bem-redux que adapta Redux para BEM. Redux es un administrador de estado que gestiona el flujo de datos. Maneja perfectamente nuestros datos dentro de interfaces simples, pero cuando desarrollamos un componente complejo, nos encontramos con el problema de representación que describí anteriormente. Redux no es amigable con i-bem, corregimos errores y estamos tristes.

Se ha convertido


Redux + Reaccionar = <3
Redux almacena datos para toda la aplicación en un solo lugar [1]:



El componente en sí mismo decide cuándo y qué datos necesita [2]:



Solo necesitamos describir los escenarios del componente [3] e indicar dónde obtener los datos para su ejecución [4]:



Y React hará el resto [5]:



Este enfoque le permite seguir el principio de responsabilidad única y encapsular la lógica del componente en el componente mismo, en lugar de difundirlo en el código de la página. Es un exito!

Tienes que pagar por todo


Para tener éxito, pagamos una gran cantidad de legado en React. Es doloroso ver cómo su código, escrito hace solo un par de meses, se vuelve obsoleto.

El hecho es que React es una biblioteca de capa de vista, no un marco completo. Puede seleccionar todas las herramientas, pero tendrá que seleccionar todas las herramientas. Y también para organizar el código usted mismo, formular enfoques para resolver problemas típicos, desarrollar un conjunto de acuerdos y escribir los complementos que faltan. Escribimos nuestros propios validadores para formularios redux y aún no hemos aprendido a trabajar con animaciones complejas. Y tratamos de tirar, escribir y reescribir. Y no lo estamos reescribiendo siempre, por lo que nuestra cartera de pedidos está creciendo.

React es lo suficientemente joven y no está listo para el desarrollo empresarial, a diferencia de BEM. Y mientras estábamos aprendiendo cómo cocinarlo, arruinamos toda nuestra cocina y nosotros mismos lo arruinamos hasta el codo. Y todavía estamos debatiendo si necesitamos flujo o no, y aún no entendemos completamente qué almacenar en la tienda y qué hay en la tienda local. Escribimos según sea necesario y vamos a conferencias para averiguar cómo hacerlo. Batimos los conos, pero avanzamos con confianza.

Bollos inesperados


La nueva pila nos permitió echar un vistazo a una serie de tareas y proporcionó formas simples de resolverlas.

CSS en JS


Era


Considere un caso simple de la vida: colorear y animar un ícono por evento, algo como esto:



El código no es nada:



Es cierto que, de acuerdo con las reglas de BEM, ya deberá distribuirlo en tres directorios:



¿Arriba? Un punto discutible. Más importante aún, en js agregamos estas clases manualmente cuando ocurren los eventos necesarios. La situación habitual, pero cuanto más personalizada o compleja sea la interfaz, más a menudo tendrá que agregar y eliminar clases. ¿Y si necesita cambiar no solo el icono, sino también el texto? No es exactamente la lógica que quieres ver en el código js:



Pero, ¿qué pasa si la duración de la animación depende de algo y se establece dinámicamente? Luego reescribiremos la animación CSS en jQuery y estaremos un poco tristes.

Se ha convertido


Componentes con estilo , ¡te amo! CSS en JS: ¡un amor! Mi codificador interno se alegra:



La modularidad se conserva, la animación CSS funciona y no el trabajo manual con clases. Una buena ventaja para la nueva pila.

Escribiendo


Era


Solíamos escribir toneladas de jsDoc. Veamos si es útil:



este ejemplo está tomado del código de producción. ¿Qué contiene el estado? No tengo idea Sí, hay un archivo Léame, pero, por desgracia, está un poco desactualizado. Sí, nos da vergüenza, pero con la documentación y los comentarios a menudo esto sucede, no son confiables. Tendrá que profundizar en el código. O no profundice y accidentalmente rompa todo. Tenemos prisa, no profundicemos, rompamos y nos sintamos tristes.

Se ha convertido


Mecanografiar vino al rescate. "Tyk" en el tipo, y todos los entresijos del método ante mis ojos. Demasiado perezoso para entender? El comprobador de precompromiso comenzará a fluir , y aún debe resolverlo.

No me gustó el flujo a primera vista. Las fechas están activadas, el gerente hace ping y usted tiene "no se puede obtener la propiedad", y aquí "falta la propiedad". Pero recientemente me dijeron que los tipos pueden ser diseñados por O_o. ¿Cómo diseñar por tipos? Algo como esto:



mi mundo se puso patas arriba. El flujo ya no es una pesadilla. Fue conveniente y útil describir los módulos API con tipos antes de escribir el código. Código confiable - ¡un buen bono!

¿Entonces no más BEM?


NoBEM está vivo y seguimos admitiendo aplicaciones en la pila BEM. Con el tiempo, también se trasladarán a Reaccionar, pero por ahora estamos preparando el camino para esto: traducimos bibliotecas de componentes, formamos un conjunto de herramientas y acuerdos, y aprendemos cómo planificar las fechas de migración.

En BEM, se implementa nuestro motor de plantillas de boletines por correo electrónico. Preparamos cartas en el servidor, y las limitaciones de la plataforma BEM descrita anteriormente no afectan a esta aplicación. Usar BEM para desarrollarlo es una solución elegante apropiada.

Además, nuestros diseñadores crean prototipos con BEM y, a veces, nos traen componentes premontados en lugar de diseños. E incluso si dejamos de escribir en BEM, todavía nos encontrará :)

Leí la primera parte. ¿Qué pasa con los codificadores?


Participé en la traducción de una de las aplicaciones de BEM a React y descubrí algo importante.

Antes de unirme a Yandex.Money, era un simple tipográfico y pasé más de un año, miles de toneladas de HTML y JSX. No tomé en serio la comunidad front-end y su mundo cambiante. No entendía por qué estudiar el primer Angular para olvidarlo mañana y estudiar el segundo. No entendí por qué cambiar jQuery.Ajax a Fetch, luego reemplazar Fetch con Axios.

Resultó que cuando transfieres un proyecto de un marco a otro, no solo estás transfiriendo el código. Tenemos que analizar y mejorar la arquitectura de la aplicación, enderezar la lógica, refactorizar. Un cambio constante de herramientas no es un intento de subirse a la ola exagerada, sino una búsqueda constante de la mejor solución que cumpla con los requisitos de la época. Un campo en desarrollo dinámico como ningún otro contribuye al desarrollo de su producto y su desarrollo profesional, respectivamente. Y la interfaz es solo un área. ¡Luchemos por ello juntos!

¡Reacciona a todos!

Source: https://habr.com/ru/post/es414183/


All Articles