Tengo un problema La aplicación está escrita en Angular y la biblioteca de componentes en React. Clonar una biblioteca demasiado cara. Por lo tanto, debe usar los componentes React en una aplicación angular con un costo mínimo. Descubrimos cómo hacerlo.
Descargo de responsabilidad
No soy especialista en Angular en absoluto. Probé la primera versión en 2017, luego miré un poco en AngularDart, y ahora he encontrado soporte de aplicaciones en una versión moderna del marco. Si te parece que las decisiones son extrañas o "de otro mundo", no te parece.
La solución presentada en el artículo aún no se ha probado en proyectos reales y es solo un concepto. Úselo bajo su propio riesgo.
El problema
Ahora apoyo y desarrollo una aplicación bastante grande en Angular 8. Además, hay un par de pequeñas aplicaciones en React y planea construir una docena más (también en React). Todas las aplicaciones se utilizan para las necesidades internas de la empresa y deben tener el mismo estilo. La única forma lógica es crear una biblioteca de componentes sobre la base de la cual pueda construir rápidamente cualquier aplicación.
Pero en la situación actual, no puede simplemente tomar y escribir una biblioteca en React. No puede usarlo en la aplicación más grande, está escrito en Angular. No soy el primero en encontrar este problema. Lo primero que debe estar en Google para "reaccionar en ángulo" es el repositorio de Microsoft . Desafortunadamente, esta solución no tiene documentación en absoluto. Además, en el archivo Léame del proyecto, se dice claramente que el paquete es utilizado internamente por Microsoft, el equipo no tiene planes obvios para su desarrollo y es solo bajo su propio riesgo utilizarlo. No estoy listo para arrastrar un paquete tan ambiguo a producción, así que decidí escribir mi bicicleta.

Sitio oficial @ angular-react / core
Solución
React es una biblioteca diseñada para resolver un problema específico: administrar el árbol DOM de un documento. Podemos poner cualquier elemento React en un nodo DOM arbitrario.
const Hello = () => <p>Hello, React!</p>; render(<Hello />, document.getElementById('#hello'));
Además, no hay restricciones en las llamadas repetidas a la función de render
. Es decir, es posible representar cada componente por separado en el DOM. Y esto es exactamente lo que ayudará a dar el primer paso en la integración.
Preparación
En primer lugar, es importante comprender que React por defecto no requiere condiciones especiales durante el ensamblaje. Si no usa JSX, pero se limita a llamar a createElement
, entonces no tendrá que tomar ninguna medida, todo saldrá de la caja.
Pero, por supuesto, estamos acostumbrados a usar JSX y no queremos perderlo. Afortunadamente, Angular usa TypeScript de manera predeterminada, que puede transformar JSX en llamadas a funciones. Solo necesita agregar el indicador del compilador --jsx=react
o en tsconfig.json
en la sección compilerOptions
agregue la línea "jsx": "react"
.
Integración de pantalla
Para comenzar, debemos asegurarnos de que los componentes React se muestren dentro de la aplicación Angular. Es decir, para que los elementos DOM resultantes del trabajo de la biblioteca ocupen los lugares correctos en el árbol de elementos.
Cada vez que usa el componente Reaccionar, pensar en llamar correctamente a la función de render
es demasiado difícil. Además, en el futuro necesitaremos integrar componentes a nivel de datos y controladores de eventos. En este caso, tiene sentido crear un componente angular que encapsule toda la lógica de crear y controlar el elemento React.
El código del componente angular es extremadamente simple. Por sí solo representa un contenedor vacío y obtiene un enlace a él. En este caso, en el momento de la inicialización, se llama el renderizado del elemento React. Se crea utilizando la función createElement
y se pasa a la función de render
, que lo coloca en el nodo DOM creado desde Angular. Puede usar dicho componente como cualquier otro componente de Angulat, sin condiciones especiales.
Integración de entrada
Por lo general, al mostrar elementos de la interfaz, debe transferirles datos. Todo aquí también es bastante prosaico: al llamar a createElement
puede pasar cualquier información a través de los accesorios al componente.
Ahora puede pasar la cadena de name
al componente Angular, caerá en el componente Reaccionar y se representará. Pero si la línea cambia debido a razones externas, React no lo sabrá y obtendremos una pantalla desactualizada. Angular tiene un ngOnChanges
ciclo de vida ngOnChanges
que le permite realizar un seguimiento de los cambios en los parámetros de los componentes y las reacciones a los ngOnChanges
. Implementamos la interfaz OnChanges
y agregamos un método:
Basta con volver a llamar a la función de render
con un elemento creado a partir de nuevos accesorios, y la propia biblioteca descubrirá qué partes del árbol deben renderizarse. El estado local dentro del componente también se conservará.
Después de estas manipulaciones, el componente angular se puede usar de la manera habitual y pasarle datos.
<app-hello name="Angular"></app-hello> <app-hello [name]="name"></app-hello>
Para un trabajo más fino con la actualización de componentes, puede mirar hacia la estrategia de detección de cambios . No consideraré esto en detalle.
Integración de salida
Otro problema persiste: la reacción de la aplicación a los eventos dentro de los componentes React. @Output
decorador @Output
y @Output
la devolución de llamada al componente a través de accesorios.
Listo Al usar el componente, puede registrar controladores de eventos y responder a ellos.
<app-hello [name]="name" (click)="sayHello($event)"></app-hello>
El resultado es un contenedor completamente funcional para el componente React para usar dentro de una aplicación angular. Puede transmitir datos y responder a eventos dentro de él.
Una cosa mas ...
Para mí, lo más conveniente en Angular es el enlace de datos bidireccional, ngModel
. Es conveniente, simple, requiere una cantidad muy pequeña de código. Pero en la implementación actual, la integración no es posible. Esto se puede arreglar. Para ser sincero, no entiendo realmente cómo funciona este mecanismo desde el punto de vista del dispositivo interno. Por lo tanto, admito que mi solución es súper subóptima y me alegrará si escribe en los comentarios una forma más hermosa de apoyar ngModel
.
En primer lugar, debe implementar la interfaz ControlValueAccessor
(del paquete @angular/forms
y agregar un nuevo proveedor al componente.
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms'; const REACT_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => HelloComponent), multi: true }; @Component({ selector: 'app-hello', template: `<div #react></div>`, providers: [PREACT_VALUE_ACCESSOR], }) export class PreactComponent implements OnInit, OnChanges, ControlValueAccessor {
Esta interfaz requiere la implementación de los métodos onBlur
, writeValue
, registerOnChange
, registerOnTouched
. Todos ellos están bien descritos en la documentación. Nos damos cuenta de ellos.
Después de eso, debe asegurarse de que todo esto se pase al componente React. Desafortunadamente, React no puede trabajar con el enlace de datos bidireccional, por lo que le daremos un valor y una devolución de llamada para cambiarlo. renderReactElement
método renderReactElement
.
Y en el componente Reaccionar, usaremos este valor y la devolución de llamada.
export const Hello = ({ name, onClick, model }) => ( <div> <p>Hello, {name}!</p> <button onClick={onClick}>Say "hello"</button> <input value={model.value} onChange={e => model.onChange(e.target.value)} /> </div> );
Ahora, realmente integramos React en Angular. Puede usar el componente resultante como desee.
<app-hello [name]="name" (click)="sayHello($event)" [(ngModel)]="name" ></app-hello>
Resumen
React es una biblioteca muy simple que es fácil de integrar con cualquier cosa. Usando el enfoque que se muestra, no solo puede usar ningún componente React dentro de las aplicaciones Angulares de forma continua, sino que también puede migrar gradualmente toda la aplicación.
En este artículo, no toqué en absoluto los problemas de estilo. Si usa soluciones clásicas de CSS en JS (componentes con estilo, emoción, JSS), no tiene que tomar ninguna acción adicional. Pero si el proyecto requiere soluciones más productivas (astroturf, linaria, módulos CSS), deberá trabajar en la configuración del paquete web. Historia destacada: personalice la configuración del paquete web en su aplicación angular .
Para migrar completamente la aplicación de Angular a React, aún debe resolver el problema de implementar servicios en React-components. Una manera simple es obtener el servicio en un componente contenedor y pasarlo a través de accesorios. La forma difícil es escribir una capa que obtenga servicios del inyector por token. Consideración de este tema más allá del alcance del artículo.
Bono
Es importante comprender que con este enfoque de 85 KB de Angular, se agregan casi 40 KB de código de react
y react-dom
. Esto puede tener un impacto significativo en la velocidad de la aplicación. Recomiendo considerar usar el Preact en miniatura, que pesa solo 3 KB. Su integración casi no es diferente.
- En
tsconfig.json
agregamos una nueva opción de compilación: "jsxFactory": "h"
, indicará que necesita usar la función h
para convertir JSX. Ahora, en cada archivo con código JSX, import { h } from 'preact'
. - Todas las llamadas a
React.createElement
reemplazan por Preact.h
. - Todas las llamadas a
ReactDOM.render
reemplazan por Preact.render
.
Hecho Lea las instrucciones para migrar de React a Preact . Prácticamente no hay diferencias.
UPD 19.9.19 16.49
Enlace temático de los comentarios - Micro Frontends
UPD 20.9.19 14.30
Otro enlace temático de los comentarios - Micro Frontends