Video Summary and Transcription
Esta charla introduce el uso de React y las tecnologías web para construir interfaces de usuario en Minecraft. Discute los desafíos de incorporar nuevos desarrolladores a la tecnología actual y los beneficios de usar estándares abiertos. El orador explica el uso de Gameface, una solución para construir interfaces de usuario de juegos con React y Webpack. La charla también cubre la gestión del estado en un entorno de juego y el uso de facetas para la optimización del rendimiento. Concluye con una visión general de la marca Oryui y la disponibilidad de recursos en GitHub.
1. Introducción al uso de React en Minecraft
Bienvenidos a Usar React para construir interfaces de usuario de juegos eficientes en Minecraft. Soy Paolo, líder técnico en Mojang Studios. Nuestro objetivo es cambiar cómo se construyen las interfaces de usuario en Minecraft introduciendo un sistema de diseño. Ya hemos implementado la pantalla de logros basada en estándares web y React. Soportamos múltiples plataformas y tipos de entrada, incluyendo VR.
Bienvenidos a Usar React para construir interfaces de usuario de juegos eficientes en Minecraft. Mi nombre es Paolo. Soy el líder técnico en Mojang Studios aquí en Estocolmo. Y trabajo en un juego con el que probablemente estén familiarizados, que es Minecraft.
Soy parte de este fantástico equipo compuesto por artistas, diseñadores, desarrolladores de C++, desarrolladores de JavaScript, y un productor. Y nuestro principal objetivo es cambiar cómo se construyen las interfaces de usuario en Minecraft. Y estamos haciendo eso no solo cambiando la pila de tecnología, sino también introduciendo un sistema de diseño en el producto.
Estos son algunos de los componentes que tenemos, y es posible que los vean implementados en los próximos años. Y sí, en realidad ya estamos incluso en producción. Implementamos una pantalla el año pasado, que es la pantalla de logros. Así que si juegan Minecraft Bedrock Edition en su Xbox o PlayStation, probablemente ya hayan visto la pantalla. Y esta pantalla está completamente basada en estándares web, y está construida usando React. Y estas son todas las plataformas que soportamos, lo cual es también uno de los principales desafíos para nuestro proyecto. Así que necesitamos funcionar en Xbox One, en PlayStation, en Switch. Tenemos Android, teléfonos y tablets, iOS, teléfonos y tablets, Windows 10, Mac OS, casi todas las plataformas que existen, Minecraft funciona en ellas. Y luego necesitamos soportarlo. Y eso no es solo acerca de las capacidades del dispositivo, sino también de diferentes tipos de entrada. Así que necesitamos soportar touch, gamepad, todo tipo de cosas. VR, incluso, es soportado por Minecraft.
2. Uso de tecnologías web para las interfaces de usuario de Minecraft
¿Por qué estamos utilizando tecnologías web para construir interfaces de usuario para Minecraft? Minecraft es un juego con motor personalizado, y la interfaz de usuario está completamente construida internamente. La incorporación de nuevos desarrolladores a la tecnología actual lleva mucho tiempo, por lo que queríamos una solución basada en estándares abiertos para una mejor mantenibilidad y una velocidad de iteración mejorada.
Pero, ¿por qué estamos utilizando tecnologías web, o debería decir estándares web, para construir interfaces de usuario para Minecraft? Hay muchas razones. Y algunas de ellas las cubrí en esta charla previa que di en 2018. Así que si tienes curiosidad, también puedes echarle un vistazo. Pero si no quieres mirar eso, aquí está el resumen. Básicamente, Minecraft es un juego con motor completamente personalizado. Así que no utiliza algo como Unreal o Unity. Así que la UI está completamente construida internamente, es completamente personalizada. Y actualmente eso ha sido un problema para nosotros cuando incorporamos a nuevos desarrolladores, les lleva mucho tiempo acostumbrarse a la tecnología y descubrir cómo usarla. Y queríamos pasar a una solución que nos llevara a una mejor mantenibilidad y que facilitara la incorporación de personas y también mejorara la velocidad de iteración. En resumen, queríamos hacer algo que estuviera basado en estándares abiertos, W3C, y queríamos obtener el beneficio de todas las grandes herramientas que el ecosistema JavaScript tiene.
3. Uso de Gameface para las interfaces de usuario de Minecraft
No estamos simplemente tomando Chromium e integrándolo en Minecraft. En cambio, estamos utilizando una solución llamada Gameface de Korean Labs. Gameface es un subconjunto de estándares web abiertos, construido para interfaces de usuario de juegos con un enfoque en el rendimiento. Es como un pequeño navegador que ejecuta interfaces de usuario construidas con React y Webpack. Los desarrolladores web que se unen a Mojang pueden ser productivos desde el primer día y contribuir a la interfaz de usuario del juego.
Pero, ¿cómo estamos haciendo esto? ¿Estamos simplemente tomando Chromium e integrándolo en Minecraft? Técnicamente eso podría ser una posibilidad, y hay algunos juegos que realmente hacen esto. Lo que estamos haciendo es algo diferente. Hay una empresa llamada Korean Labs, y han estado haciendo soluciones basadas en Chromium durante un tiempo, para las interfaces de usuario de los juegos. Pero ellos, desde hace un tiempo, construyeron otra solución a la que llaman Gameface, donde tomaron los standards abiertos de la web, y luego construyeron solo un subconjunto de ellos en algo que llaman Gameface. Así que Gameface, podría considerarse algo así como un pequeño navegador, diría yo. Si construyes una UI para Gameface, es muy probable que funcione en Chrome o Firefox, pero puede que no sea el caso si simplemente eliges una UI aleatoria que construiste en Firefox y la pones en Gameface, puede que no funcione. Por ejemplo, tiene soporte para Flexbox, pero no tiene soporte para Floats. Y está construido con el objetivo principal de dirigirse a las interfaces de usuario de los juegos, por lo que performance es lo principal, es uno de los principales objetivos. Pero, aparte de sacar a Gameface de la ecuación, es una pila bastante estándar. Usamos React, usamos Webpack, hacemos unit tests con Jest. Así que si eres un desarrollador web que trabaja en una masterclass hoy y te unes a Mojang, probablemente serás productivo desde el primer día y podrás contribuir con una UI para el juego, lo cual es bastante impresionante y es una experiencia muy diferente a la que tenemos hoy en el estudio.
4. Gestión de Estado y Facetas en el Entorno del Juego
Antes de discutir el rendimiento, entendamos cómo funciona la gestión de estado en un entorno de juego. En las aplicaciones web tradicionales, el estado se copia en el navegador, pero en un mundo de juego, el motor de juego C++ contiene el estado. Minecraft sirve como un buen ejemplo. El HUD muestra la salud, la saciedad y los elementos seleccionados, que pueden cambiar en función de la interacción del jugador. Nos referimos a estas porciones de datos como facetas. Las facetas son similares a los observables y proporcionan actualizaciones a medida que cambian los valores. El estado debe vivir en el lado C++, creando un almacén global. Nos inspiramos en Recoil y Jotai para construir nuestra solución, RackFacet, con algunas diferencias. RackFacet es una colección de paquetes que incluye RackFacet Remote para la comunicación entre JavaScript y C++.
Pero antes de hablar sobre performance, que es el tema principal que queremos discutir en esta presentación, primero necesitamos hablar sobre cómo funciona la state management y cómo es diferente cuando estamos en un entorno de juego.
Entonces, cuando miramos una aplicación web tradicional, podemos pensar en ella de esta manera. Tenemos tu navegador, por ejemplo, aquí tengo Firefox ejecutando la UI y eso tiene su propio entorno de ejecución de JavaScript y luego tenemos, digamos, un servidor en internet ejecutando Node.js y luego tenemos solicitudes HTTP y respuestas que regresan. Así que es una especie de separación de solicitud-respuesta y luego tenemos una copia del estado en el navegador.
Cuando entras en un mundo de juego, lo que usualmente tenemos es solo un solo proceso, ¿verdad? Tenemos el motor de JavaScript todavía en funcionamiento, que tendrá nuestro código de React y nuestro código de UI en él, pero tenemos el motor de juego C++ justo allí y eso es lo que llamamos nuestro backend y eso es generalmente el titular del estado. Si quieres entender esto, es mejor mirar un ejemplo y un buen ejemplo es Minecraft.
Entonces, si miras esta pantalla, que es básicamente lo que ves si estás jugando el juego, esto es el HUD. Vemos básicamente la información sobre cuánta health tiene tu personaje, la saciedad porque Minecraft es un juego de supervivencia, así que si no comes, empiezas a pasar hambre y puedes morir. También vemos los elementos a los que tienes acceso en tu barra de acceso rápido, así que estos son elementos que puedes cambiar rápidamente usando el gamepad, y qué elemento tienes seleccionado. Así que aquí puedes ver que tenemos el primer elemento seleccionado, que es la espada, pero un jugador también puede, interactuando con el juego, cambiar el elemento seleccionado a la Carne Podrida y eso no solo afecta al juego en sí, sino que también necesita ser reflejado en la UI. Y el jugador también puede recibir daño, ¿verdad? Digamos que un mob viene y un creeper explota al lado del jugador, y entonces necesitamos restar algo de health y aquí la health ha bajado a ocho. Así que podemos ver aquí que tenemos algo así como dos categorías de data, dos grupos de data. Y a esos los llamamos facetas.
Así que facetas es un término que acuñamos internamente en el estudio porque necesitábamos algo que no existía dentro de la base de code. Así que no podíamos usar modelo, por ejemplo. Pero esto es más o menos lo que es. Es solo una porción de data. Pero así como eso, como las facetas conceptualmente, son muy similares a un observable donde disponible con el tiempo. Así que nos suscribimos a una faceta, como digamos las estadísticas del jugador, y luego seguimos recibiendo actualizaciones sobre nuevos valores a medida que cambian, a medida que el jugador juega el juego, ¿verdad? Así que en la parte de abajo podemos ver cómo a medida que avanza el tiempo y el jugador pierde health, tenemos un componente hipotético de health del jugador que se actualiza con cuánta health tiene el jugador. Conceptualmente entonces lo que queremos es que el estado no viva en el lado de JavaScript, sino que más bien debería vivir en el lado de C++, que es nuestro backend, pero está viviendo justo allí dentro del mismo proceso. Queremos algo así como un almacén global y ese almacén global es el lado C++ del juego. Y cuando pensamos en almacenes globales, y miramos el ecosistema de JavaScript, hay un par de soluciones disponibles. Algunas de las más populares en estos días son Recoil o Jotai y son muy parecidas a esta moda donde tienes un almacén global. Y tomamos un enfoque muy similar a estas soluciones, y nos inspiramos en la API de Recoil's para construir algo que se siente muy familiar para que cualquiera que venga al estudio entienda qué conceptos estamos tratando de implicar. Pero hay algunas diferencias, por supuesto, que vamos a repasar en Next. Así que RackFacet es en realidad un pack, una colección de paquetes. Tenemos muchos paquetes. La parte que se ocupa de la comunicación entre los bits de JavaScript y los bits de C++ la llamamos RackFacet Remote. Y esa es la primera parte de la que voy a hablar ahora. Y supongo que lo primero que necesitamos mirar es cómo definimos las facetas, ¿verdad, los bits de estado que tenemos que se comparten entre el lado C++ y el lado de JavaScript.
5. Uso de Facetas Remotas y Optimización del Rendimiento
Definimos el contrato de la API, utilizamos selectores para seleccionar facetas y las convertimos en estados de React. Nuestra solución requiere un proveedor para implementar el pegamento de JavaScript a C++. Configuramos oyentes y emitimos eventos para acceder y notificar datos. Las suscripciones y las funciones de limpieza aseguran actualizaciones eficientes. React ya es rápido.
Y si miraste... Si has usado Recall.js, por ejemplo, en algún momento, esto podría parecerte un poco familiar. Lo único es que estamos usando TypeScript aquí. Así que lo primero con lo que empezamos es definiendo el contrato de la API, que es simplemente una interfaz para este tipo de data. Esto es algo de lo que normalmente hablamos con los desarrolladores de C++ en una línea. Así que decimos, por ejemplo, que queremos tener una health que tenga un número y queremos tener una plenitud que tenga un número. Luego tenemos un identificador único. Y así es como, desde el lado de JavaScript, podemos solicitar una pieza particular de data al lado de C++. Luego tenemos la definición en sí, que es muy similar a un átomo, en un lenguaje de Recall.js. Y luego también tenemos selectores, que nos permiten seleccionar la faceta completa y cortar solo una pieza de ella, algo así como una función de mapa. Y aquí estamos seleccionando, por ejemplo, solo el atributo health de toda la faceta de estadísticas del lugar.
Y cuando queremos consumirlo, también es muy familiar, tendríamos solo algunos ganchos extra y algunos nombres diferentes aquí. Pero básicamente necesitamos primero usar el gancho useRemoteFacet para suscribirnos a esta faceta remota, para realmente iniciar y empezar a escuchar los eventos. Luego lo convertimos en un estado de React, que es la health allí, va a ser la cadena real donde el número que contiene la health, y luego podemos simplemente renderizar el resultado como un componente normal de React. La principal diferencia entre nuestra solución y las soluciones existentes en el espacio de código abierto es la suscripción. Nuestras facetas, realmente no tienen ningún valor hasta que empezamos a suscribirnos a ellas. Y después de eso, actúan prácticamente como observables. Así que en nuestra solución, tenemos que tener un proveedor que envuelva la aplicación y este proveedor, tenemos la responsabilidad de implementar el pegamento desde el lado de JavaScript hasta el lado de C++. Así que en este caso aquí, tenemos un motor, que es como un objeto global al que tenemos acceso. Y es algo así como un emisor de eventos, donde necesito desde el lado de C++, desde el lado de JavaScript, necesito configurar un oyente, que en este caso está escuchando el evento facet-updated player stats. Digamos que quiero acceder a los data para las estadísticas del jugador. Y luego necesito emitir un evento para notificar al lado de C++ o al game engine que estoy interesado en esos data. Y finalmente, puedo devolver una función que puede hacer una limpieza. Así que lo que sucede en la práctica es que cuando un componente se suscribe a, digamos, el selector de health del jugador que mostramos en el ejemplo anterior, si es la primera vez que esta función se llamará, configuraremos la suscripción y empezaremos a escuchar esos data. Y una vez que ese componente se desmonta, se llama a la función de limpieza y dejamos de escuchar. Y le decimos al lado de C++ que deje de notificarnos las actualizaciones. Eso es bastante genial. Y funciona bastante bien para nosotros.
Así que el siguiente paso, que es la parte interesante para todos ustedes es, por supuesto, cómo manejamos el performance. ¿Dónde optimizas las cosas para una solución para que pueda funcionar bien en un mundo de desarrollo de juegos? Y lo primero que supongo que hay que dejar claro es que React por sí mismo ya es bastante rápido.
6. Optimizando la Reconciliación de React con Actualizaciones de Facetas
Al construir aplicaciones web con React, a menudo intentamos evitar re-renderizados innecesarios y mejorar el rendimiento. En este caso, proponemos omitir la reconciliación de React actualizando directamente el DOM desde las actualizaciones de facetas. Demostramos este enfoque con un ejemplo de una barra de progreso. Para lograr esto, nos suscribimos a las facetas utilizando el gancho use remote facet y transformamos el valor de la faceta utilizando el gancho use facet map. Sin embargo, cuando pasamos el valor transformado al div, React puede no reconocer el tipo de datos.
Si estás construyendo aplicaciones web, generalmente es suficiente usar simplemente React tal como es. Y soluciones como Recoil o Jotite, ayudan a reducir y seguir actualizaciones para que solo partes específicas de tu árbol de React se actualicen. Y cuando nos encontramos con problemas de performance en React, generalmente la recomendación es que revisemos nuestros componentes y veamos si tenemos algún tipo de re-renderizado que estamos haciendo innecesariamente y veamos si podemos evitar eso a toda costa.
Entonces, cuando miramos este concepto, es como, quiero decir, ¿qué pasaría si en lugar de simplemente evitar renderizados innecesarios, pudiéramos evitar todos los re-renderizados en general? ¿O podríamos tener una situación en la que básicamente no activamos la reconciliación de React? Entonces, aquí está cómo está sucediendo como nuestro ejemplo anterior, ¿verdad? La faceta se actualiza a través de un evento desde el lado de C++ que activa, que convierte, que por supuesto actualiza algún estado internamente en React, lo que activa la reconciliación y luego React a través del DOM virtual, descubre, oh, necesito actualizar este nodo de texto y así es como ocurre una actualización. Entonces, lo que estamos proponiendo es que podríamos simplemente saltarnos el intermediario e ir directamente desde la actualización de la faceta a la actualización del DOM. Entonces, si volvemos al ejemplo que teníamos antes, lo que estamos proponiendo es en lugar de tomar la faceta, que es una agradable estructura de data que contiene un valor a lo largo del tiempo, en lugar de desempaquetarla y convertirla en un estado, un estado de React, ¿podemos simplemente tomar la faceta y pasarla directamente al renderizado de React y dejar que el renderizado de React descubra cómo desempaquetar el valor, desempaquetar el valor de la faceta? Y eso es exactamente lo que hicimos. Entonces, queremos pasar la faceta, y eso actualiza directamente un nodo de texto. Entonces, veamos cómo lo hemos hecho. Entonces, tomando el ejemplo de antes, simplemente lo amplié un poco y lo hice un poco más interesante. Así que aquí tengo ahora una barra de progreso un poco más compleja. Así que en realidad estoy renderizando algunos divs para hacerlo con un estilo agradable. Puedes ver en la parte superior cómo se verá visualmente. Y luego vemos el resultado codificado. Tenemos una variable codificada allí con el progreso establecido en dos. Así que es un componente de React muy simple, solo renderizando una barra de progreso. Entonces, si queremos tomar este ejemplo y hacerlo de manera que omita completamente la reconciliación, ¿cómo podríamos hacer eso? Entonces, creo que el primer paso, por supuesto, es suscribirse a las facetas en el primer paso. Así que cambiamos eso para hacer el uso de remote facet, para tomar el valor del selector, y agarrarlo para ser utilizado en un componente. Pero el progreso aquí no es realmente un número per se, es en realidad solo la faceta todavía, como es una estructura de data que contiene el valor dentro de ella. Así que estoy obteniendo un error de tipo aquí porque no puedo hacer una operación aritmética con una faceta. No es un número. Entonces necesitamos descubrir una forma ahora de decir, bien, tengo esta faceta y quiero hacerle alguna transformación y convertirla en algo más. Si esto fuera un recoil en un estado global, probablemente solo usaría un selector. Como esto está dentro de un componente de React, lo que necesitamos es alguna forma de hacer un selector que se defina dentro de un componente de React. Y lo apoyamos a través de un gancho personalizado que tenemos que se llama use facet map, y es muy similar a un selector conceptualmente. Toma una faceta de entrada, en realidad puede tomar varias, pero en este caso, estamos pasando solo una, que es el progreso al que nos suscribimos en la línea anterior. Luego tenemos una función de mapa que tomará el progreso, que es el número, que viene de la faceta, y lo transformará en la cadena que contiene la unidad de píxel. Y el resultado de eso es también otra faceta, que ahora está sosteniendo en la variable de ancho. Entonces, el siguiente paso, por supuesto, es tomar este ancho y pasarlo al div. Pero si hacemos eso, React no va a estar contento. Va a ser como, hey, no sé qué hacer con este tipo de data, ¿verdad? Estás tratando de darme algo que no es una cadena, no es un número.
7. Renderizado con FastDiv y Facetas
Introdujimos un nuevo div llamado FastDiv y creamos una serie de componentes que aceptan facetas como props. Esto nos permite crear un componente que se suscribe a una faceta remota y la mapea a una faceta de ancho. Hemos creado un renderizador que admite facetas como valores para props e incluso puede hacer componentes personalizados que admiten nativamente las facetas.
No sé cómo renderizar esto. Entonces, el siguiente paso en esto es que necesitamos cambiar el renderizador de React en sí para admitir este tipo de data. Y eso es lo que hicimos, al introducir un nuevo div al que llamamos FastDiv. Y en realidad creamos una serie completa de componentes que coinciden, algo así como uno a uno. Así que tenemos un FastB, un FastSpan. En lugar de aceptar solo números regulares, cadenas para las props, también aceptan facetas como una prop. Y al final, lo que tenemos es básicamente esto, ¿verdad? Un componente que se está suscribiendo a una faceta remota, convirtiéndose en una variable de progreso que contiene una faceta de progreso. Luego estamos mapeando eso en una faceta de ancho que tiene el ancho. Y luego estoy tomando eso y lo paso al FastDiff que luego actualiza esa prop en el DOM. Básicamente hemos creado un renderizador que admite nativamente las facetas, o podrías decir un observable ligero, como valores para las props. También podríamos ir un paso más allá y hacer componentes personalizados que también admiten nativamente las facetas como valor.
8. Uso de Facetas para Mejorar el Rendimiento
Veamos cómo el uso de facetas mejora el rendimiento. Al extraer el componente de la barra de progreso y pasar el progreso como una faceta, evitamos desencadenar re-renderizados innecesarios. La implementación detrás de escena utiliza un renderizado personalizado y está disponible en el paquete react-facet-dom-fiber. Las pruebas de rendimiento en Xbox One y Chrome muestran que la solución basada en facetas es significativamente más rápida, tomando solo el 32% del tiempo en comparación con la implementación de React basada en estado.
Veamos cómo se vería eso. Si volvemos atrás y echamos un vistazo a este ejemplo del jugador health, ¿qué pasaría si queremos hacer también la plenitud? Queremos renderizar también la barra de progreso de plenitud. ¿Podríamos hacer eso? Supongo que lo primero es que queremos extraer los componentes para que podamos reutilizarlos en ambos lugares. Queremos tomar esta parte aquí que maneja la barra de progreso y extraerla a un componente de barra de progreso. Pero queremos pasar el progreso allí como una faceta. No queremos pasar como un número. Porque si desempaquetamos eso en la salud del jugador para convertirlo en un estado regular de React volveríamos básicamente a desencadenar la conciliación, y derrotaríamos todos los beneficios que estábamos tratando de lograr.
Así que aquí los tenemos, básicamente la salud del jugador y la plenitud del jugador, dos componentes usando la barra de progreso, y ambos pasando un progreso, cada uno con su propia faceta que contiene sus propios data. Y la implementación de la barra de progreso se ve así. En lugar de tomar un progreso que es solo un número, está tomando en su lugar una faceta de un número. Y tenemos el tipo específicamente disponible para allí. Pero por lo demás, la implementación aquí en el cuerpo es exactamente la misma que teníamos antes. Así que esto es más o menos lo que está sucediendo al final. Tenemos en la parte superior nuestra faceta remota de estadísticas de jugador. Una vez que se notifica de una actualización desde el lado de C++, esos data luego se pasan hacia abajo al selector remoto de salud del jugador que luego elige solo la health de los data. Eso luego se pasa a su uso de faceta remota, que desencadena la suscripción. Luego pasa por el uso de mapeo de facetas que transforma eso. Y luego de la forma en que finalmente llega al div de facetas, y luego se actualiza el DOM. Y lo genial de todo esto es que todo esto está sucediendo detrás de escena. No estamos desencadenando ningún re-renderizado en absoluto. Y toda esta implementación está construida como un renderizado personalizado, como dije, y está disponible en este paquete, react-facet-dom-fiber.
Pero todo esto es fantástico y es bastante genial, pero ¿cuánto mejora realmente el performance? Así que comparando, y puse aquí dos plataformas diferentes solo para darle perspectiva. Así que estoy ejecutando estos escenarios tanto en una Xbox One como en Chrome. Así que esta Xbox One está ejecutando el juego de Minecraft, y también tenemos Chrome en mi MacBook. Este ejemplo que estoy mostrando aquí básicamente está tomando la implementación de la barra de progreso que hicimos antes y simplemente cambiando su valor en cada cuadro y viendo cuánto tiempo estoy tomando entre cada implementación. A la izquierda, tenemos una implementación basada en el estado de React y ha tomado como el 100% del tiempo. Y en la Xbox One, podemos ver que una solución basada en Facetas toma solo el 32% del tiempo para hacer el mismo trabajo. Cuando miramos en Chrome, la diferencia es un poco menor, pero también es mucho más rápido cuando miramos la implementación de Facetas. Otro escenario que es muy interesante de ver es que imagina si tienes como una gran lista y la estás ejecutando a través de un montón de componentes y la estás memorizando. Así que cada componente está memorizado.
9. Comparación de Rendimiento con Facetas
Cuando comparamos la solución basada en Facetas en la Xbox con la implementación estándar de React, la diferencia es enorme, siendo la Xbox significativamente más rápida. Plataformas como Xbox, PlayStation y Switch, que no tienen JIT habilitado, pueden tener diferentes características de rendimiento. Cuanto más complejos se vuelven los árboles de React, mayor es la brecha de rendimiento. Más información y ejemplos están disponibles en nuestro repositorio de GitHub.
Cuando observamos la solución basada en Facetas en la Xbox, la diferencia se vuelve masiva, tomando solo el 12% del tiempo. Y en Chrome, también es alrededor del 30% del tiempo. Este ejemplo lo mencioné porque es bastante interesante ver si tenemos una lista grande pero todos los elementos se actualizan a la vez, aquí podemos ver que la Xbox es en realidad mucho más rápida cuando la comparamos con la implementación estándar de React. Cuando miré Chrome en mi MacBook, la diferencia no es tan grande. Y lo principal que vale la pena mencionar es que cuando estamos ejecutando V8 en nuestro Chrome, en nuestras Macs, tenemos compilación justo a tiempo. Pero desafortunadamente en algunas plataformas como la Xbox, PlayStation y Switch, no podemos tener JIT habilitado. Por lo tanto, las optimizaciones y las bibliotecas a las que estamos acostumbrados se comportarán y tendrán diferentes características de rendimiento cuando las ejecutamos. Entonces, y también otra cosa importante es que cuanto más complejos se vuelven los árboles de React, entonces la brecha será más amplia y en realidad la implementación será más rápida en relación con la implementación estándar de React. Tenemos más datos, por supuesto, disponibles en nuestro repositorio de GitHub. Todos los ejemplos que mencioné aquí, puedes ir allí y ver sus implementaciones reales para entender mejor exactamente lo que están haciendo. No tengo mucho tiempo para entrar en detalles aquí, pero tenemos todo eso disponible en el repositorio de GitHub.
10. Introducción a Facets y Oryui
Las Facets proporcionan una API más amigable con React con hooks que aceptan y devuelven facets. El paquete react-facet-core incluye estos hooks, como useFacetState y useFacetCallback. Las Facets son un conjunto de bibliotecas, incluyendo el tipo de datos observable ligero y el paquete remoto para compartir el estado entre JavaScript y C++. También tenemos un renderizador personalizado dentro de DOM fiber. Empezar con las facets es fácil usando el paquete de componentes DOM, que implementa componentes rápidos como componentes regulares de React. Estamos abriendo el código fuente bajo la marca Oryui y contratando para emocionantes posiciones en Mojang. Visita nuestro repositorio de GitHub para más información.
Pero no solo eso, quiero decir, si miras las facets, tenemos tiendas globales, en realidad tenemos un montón de hooks más que se convierten en una API mucho más amigable con React. Así que para todos los hooks de React que están a la derecha aquí, como use state, useReduce, o useEffect, tenemos implementaciones equivalentes en el mundo de las facets, pero en lugar de, ya sabes, la diferencia es que en realidad pueden aceptar facets como entrada, y podrían devolver una facet como salida. Y todo esto está disponible en el paquete react-facet-core.
Solo para ir realmente rápido, aquí está cómo se ve eso, donde tengo este componente aquí, donde estoy usando useFacetState, que es muy similar a useState, pero en lugar de que el valor sea solo una cadena, sería, por ejemplo, una facet de una cadena. Luego también tengo, useFacetCallback aquí, que es muy similar a useCallback, pero en lugar de eso te permite pasar un valor allí, que es una facet en lugar de eso. Hay más hooks y documentación disponible también en nuestra página de GitHub.
Así que en resumen, una facet no es solo una biblioteca, sino un conjunto de bibliotecas. Tenemos el nuevo tipo de datos que introdujimos, al que llamamos facets, por supuesto, que es un observable ligero que vive dentro del núcleo de la facet de React, y puedes usar solo el núcleo de la facet de React para construir algo sin necesariamente usar el renderizador personalizado o algo así o el paquete remoto. Tenemos el paquete remoto, que es el mecanismo que desarrollamos para tener como esta tienda de estado global para compartir el estado entre el lado de JavaScript y el lado de C++, y tenemos nuestro renderizador personalizado que construimos dentro de la fibra DOM. Así que estos son todos los paquetes que tenemos, y puedes echar un vistazo a la documentación para descubrir lo que hacen. No tengo mucho tiempo en la charla para repasarlos.
Pero parece un poco desalentador, pero en realidad es bastante fácil empezar si solo quieres mojarte los pies con las facets. Tenemos un paquete llamado componentes DOM. Lo que te permite hacer es implementar estos componentes rápidos que hicimos, pero en lugar de ser como componentes de host en un renderizador personalizado de React, en realidad son solo componentes regulares de React. Así que puedes ver aquí cómo se usan en un DOM regular de React para renderizar este componente. Y todo esto está siendo de código abierto. Estamos abriendo el código fuente bajo la marca Oryui. Oryui es una especie de iniciativa que estamos haciendo en los estudios Mojang para intentar estandarizar el desarrollo de UI en la industria del juego. Así que lo que estamos haciendo es tratar de impulsar eso. Así que Oryui tendrá facets de React para empezar, pero impulsaremos otros paquetes que estamos desarrollando también. Así que este es el fantástico logo. Y este es nuestro repositorio de GitHub. Así que si quieres mirar más rápido la base de código, si quieres aprender más, ve a GitHub y echa un vistazo a Mojang slash Oryui. Y por supuesto tengo que mencionar que estamos contratando en Mojang. Es un proyecto bastante emocionante. Quiero decir, no es en todos los lugares donde puedes ir y usar tus habilidades web para contribuir a uno de los juegos más grandes del mundo. Es bastante emocionante poder trabajar aquí. Y si quieres hacerlo, tenemos puestos abiertos para líderes técnicos, desarrolladores web. No dudes en contactarme o ir directamente a jobsmojang.com. Así que sí, muchas gracias. Tuve que correr un poco a través de las diapositivas, pero sí, si tienes alguna pregunta, creo que vamos a tener una sesión de preguntas y respuestas pronto. Así que muchas gracias y no dudes en contactarme también si tienes alguna pregunta. Adiós.
Comments