[Traducción] Proceso de diseño de Vue3

Alrededor de las 3:30 de la mañana de hoy, You Yuxi publicó un artículo en su Weibo.
Por supuesto, el jefe está en otra zona horaria. Nuestra madrugada debe corresponder a la tarde en la zona horaria donde la epidemia es más severa.

Enlace original: https://increment.com/frontend/making-vue-3/

Lecciones aprendidas al refactorizar la nueva versión de Vue.js

El año pasado, el equipo de Vue ha estado trabajando en la próxima versión principal de Vue.js y esperamos lanzar esta versión en la primera mitad de 2020. (En el momento de redactar este informe, este trabajo aún está en curso). La versión principal de Vue se formó a fines de 2018, cuando el código base de Vue 2 tenía dos años y medio. Puede que no suene largo en el ciclo de vida del software de propósito general, pero durante este período, el entorno de front-end ha experimentado cambios tremendos.

Hay dos consideraciones principales que nos llevaron a desarrollar una nueva versión principal de Vue (y reescribirla): Primero, los navegadores convencionales generalmente brindan nuevas características del lenguaje JavaScript. En segundo lugar, con el tiempo, los problemas de diseño y arquitectura en la base de código actual se han ido exponiendo gradualmente.

Por que refactorizar

Aproveche las nuevas funciones de idioma

Con la estandarización de ES6, JavaScript (formalmente conocido como ECMAScript, abreviado como ES) se ha mejorado enormemente y los navegadores convencionales finalmente han comenzado a brindar un buen soporte para estas nuevas características. En particular, algunas características nuevas nos brindan la oportunidad de mejorar en gran medida la funcionalidad de Vue.

El más notable de ellos es Proxy, que permite que el marco intercepte operaciones en objetos. La función principal de Vue es la capacidad de monitorear los cambios en el estado definido por el usuario y actualizar el DOM con datos. Vue 2 logra esta capacidad reemplazando propiedades en objetos de estado con getters y setters. Cambiar a un proxy nos permitirá eliminar las limitaciones existentes de Vue, como la incapacidad de detectar nuevas adiciones de propiedades y proporcionar un mejor rendimiento.

Sin embargo, el proxy es una función del lenguaje en sí y no se puede rellenar completamente en navegadores más antiguos. Para aprovecharlo, sabíamos que teníamos que ajustar el rango de compatibilidad del navegador del marco, lo cual fue un gran avance y solo podría lanzarse en una nueva versión principal.

Resolver problemas arquitectónicos

Resolver estos problemas en la base de código actual requerirá una refactorización arriesgada, que es casi equivalente a una reescritura completa.
En el proceso de mantenimiento de Vue 2, debido a las limitaciones de la arquitectura existente, hemos acumulado muchos problemas difíciles. Por ejemplo, la forma en que está escrito el compilador de plantillas hace que sea muy difícil admitir mapas de origen. De manera similar, aunque Vue 2 técnicamente permite construir renderizadores de alto nivel para plataformas que no son DOM, tenemos que derivar una base de código y copiar mucho código para lograr esto. Resolver estos problemas en la base de código actual requerirá una refactorización arriesgada, que es casi equivalente a una reescritura completa.

Al mismo tiempo, hemos acumulado peligros ocultos en forma de acoplamiento implícito entre los componentes internos de varios módulos y el código flotante, y el código flotante no parece pertenecer a ninguna parte. Esto hace que sea más difícil comprender partes del código base de forma aislada, y notamos que los colaboradores rara vez se sienten seguros acerca de los cambios importantes. La refactorización nos dará la oportunidad de repensar la organización del código en función de estas consideraciones.

Etapa inicial del prototipo

Comenzamos a crear prototipos de Vue 3 a fines de 2018, con el objetivo inicial de verificar las soluciones a estos problemas. En esta etapa, principalmente establecemos una base sólida para un mayor desarrollo.

Cambiar a TypeScript

Vue 2 se escribió originalmente en JS puro. Poco después de la fase de desarrollo del prototipo, nos dimos cuenta de que el sistema de tipos sería muy útil para proyectos de este tamaño. La verificación de tipos reduce en gran medida la posibilidad de introducir errores inesperados durante la refactorización y ayuda a los colaboradores a realizar cambios importantes con más confianza. Pasamos la verificación del tipo de flujo de Facebook porque se puede agregar gradualmente a los proyectos JS existentes. El flujo ayudó hasta cierto punto, pero no obtuvimos muchos beneficios de él. Especialmente los requisitos cambiantes hacen que la actualización sea muy dolorosa. En comparación con la profunda integración de TypeScript y Visual Studio Code, el soporte de Flow para entornos de desarrollo integrados no es ideal.

También notamos que los usuarios usan cada vez más Vue y TypeScript al mismo tiempo. Para respaldar sus casos de uso, debemos crear y mantener declaraciones de TypeScript por separado del uso de diferentes sistemas de tipos. Cambiar a TypeScript nos permitirá generar automáticamente archivos de declaración, reduciendo así la carga de mantenimiento.

Desacoplamiento de envases internos

También adoptamos monorepo, donde el marco consiste en paquetes de software internos, cada uno de los cuales tiene su propia API, definición de tipo y pruebas independientes. Queremos que las dependencias entre estos módulos sean más claras, para que sea más fácil para los desarrolladores leer, comprender y realizar todos los cambios. Esta es la clave de nuestros esfuerzos para reducir las barreras de contribución del proyecto y mejorar su capacidad de mantenimiento a largo plazo.

Configurar el proceso de RFC

A finales de 2018, teníamos un prototipo funcional que utilizaba el nuevo sistema de visualización basado en datos y el renderizador DOM virtual. Hemos verificado las mejoras de la arquitectura interna que queremos hacer, pero solo incluimos borradores de los cambios de API de cara al público. Ahora es el momento de convertirlos en diseños concretos.

Sabemos que debemos hacer esto temprano y con cuidado. El uso generalizado de Vue significa que los cambios revolucionarios pueden generar una gran cantidad de costos de migración de usuarios y una posible fragmentación del ecosistema. Para garantizar que los usuarios puedan proporcionar comentarios sobre cambios importantes, adoptamos un proceso RFC (Solicitud de comentarios) a principios de 2019. Cada RFC sigue una plantilla, que se centra en la motivación, los detalles del diseño, las compensaciones y las estrategias de adopción. Dado que este proceso se lleva a cabo en el repositorio de GitHub, la propuesta se envía como una solicitud de extracción, por lo que la discusión se desarrollará naturalmente en los comentarios.

El proceso RFC ha demostrado ser de gran ayuda. Como marco ideológico, nos obliga a considerar completamente todos los aspectos de los cambios potenciales, involucrar a nuestra comunidad en el proceso de diseño y presentar requisitos funcionales bien pensados.

Más rápido y más pequeño

El rendimiento es fundamental para el marco de front-end. Aunque Vue 2 tiene un rendimiento excelente, al probar nuevas estrategias de renderizado, la refactorización brinda la posibilidad de un mayor desarrollo.

Supere el cuello de botella del DOM virtual

Vue tiene una estrategia de representación bastante única: proporciona una sintaxis de plantilla similar a HTML, pero compila la plantilla en una función de representación que devuelve un árbol DOM virtual. El marco atraviesa de forma recursiva dos árboles DOM virtuales y compara cada atributo en cada nodo para determinar qué partes del DOM real deben actualizarse. Debido a que los motores JavaScript modernos realizan optimizaciones avanzadas, este algoritmo algo brutal suele ser rápido, pero aún implica mucho trabajo innecesario de la CPU. Cuando observa una plantilla que contiene una gran cantidad de contenido estático y solo una pequeña cantidad de enlace dinámico (todo el DOM virtual), es ineficiente, especialmente si solo hay un pequeño cambio pero aún necesita recurrir a todo el DOM virtual árbol para comprender qué ha cambiado.

Afortunadamente, el paso de compilación de la plantilla nos brinda la oportunidad de analizar estáticamente la plantilla y extraer información sobre las partes dinámicas. Vue 2 hace esto hasta cierto punto omitiendo subárboles estáticos, pero debido a la arquitectura del compilador demasiado simple, es difícil implementar optimizaciones más avanzadas. En Vue 3, reescribimos el compilador con una canalización de conversión AST adecuada, que nos permite escribir optimizaciones en tiempo de compilación en forma de complementos de conversión.

Con la nueva arquitectura, esperamos encontrar una estrategia de renderizado para minimizar la sobrecarga. Una opción es abandonar el DOM virtual y generar directamente operaciones DOM imperativas, pero esto eliminará la capacidad de escribir directamente funciones de representación del DOM virtual, que consideramos muy valiosas para los usuarios avanzados y los autores de bibliotecas. Además, este será un gran cambio revolucionario.

En segundo lugar, la mejor manera es eliminar el cruce del árbol DOM virtual innecesario y la comparación de atributos, que tiende a generar la mayor sobrecarga de rendimiento durante el proceso de actualización. Para lograr esto, el compilador y el tiempo de ejecución deben trabajar juntos: el compilador analiza la plantilla y genera código con sugerencias de optimización, y el tiempo de ejecución recogerá las sugerencias y tomará la ruta rápida cuando sea posible. Aquí hay tres tareas principales de optimización:

Primero, a nivel de árbol, notamos que la estructura del nodo es completamente estática cuando no hay instrucciones de plantilla (por ejemplo, v-if y v-for). Si dividimos la plantilla en "bloques" dinámicos y estáticos, la estructura del nodo dentro de cada bloque se vuelve completamente estática nuevamente. Cuando actualizamos los nodos en un bloque, ya no necesitamos atravesar el árbol de forma recursiva, porque podemos rastrear el enlace dinámico dentro del bloque en una matriz plana. Al reducir la cantidad de recorrido del árbol que necesitamos realizar en un orden de magnitud, se ahorra la mayor parte de la sobrecarga del DOM virtual.

En segundo lugar, el compilador detectará activamente nodos, subárboles e incluso objetos de datos estáticos en la plantilla, y los promoverá más allá de la función de renderizado en el código generado. Esto evita volver a crear estos objetos en cada renderizado, lo que mejora en gran medida el uso de la memoria y reduce la frecuencia de recolección de basura.

En tercer lugar, a nivel de elemento, el compilador también genera una marca de optimización para cada elemento con enlace dinámico en función del tipo de actualización que se debe realizar. Por ejemplo, un elemento con enlace de clase dinámico y muchas propiedades estáticas recibirá una bandera que indica que solo se requiere verificación de clase. El motor de ejecución obtendrá estas indicaciones y utilizará una ruta rápida dedicada.

En resumen, estas tecnologías han mejorado significativamente nuestras actualizaciones de renderizado, y ejecutar Vue 3 a veces puede ser diez veces más rápido que Vue 2.

Tamaño extremadamente pequeño

El tamaño del marco también afecta su rendimiento. Esta es una preocupación importante para las aplicaciones web, porque los recursos deben descargarse dinámicamente y la aplicación será interactiva antes de que el navegador analice el JavaScript necesario. Esto es especialmente cierto para las aplicaciones de una sola página. Aunque Vue siempre ha sido relativamente ligero (el tamaño del tiempo de ejecución de Vue 2 está comprimido a 23 KB), hemos notado dos problemas:

En primer lugar, no todo el mundo utiliza todas las funciones del marco. Por ejemplo, las aplicaciones que nunca han usado componentes de transición aún descargarán código relacionado con la transición y tomarán tiempo para analizarlo.

En segundo lugar, cuando agregamos nuevas funciones, el marco se volverá infinitamente más grande. Cuando consideramos la adición de nuevas funciones, tenemos que considerar el tamaño del problema. Por lo tanto, tendemos a incluir solo las funciones que la mayoría de los usuarios usarán en el marco.

Idealmente, los usuarios deberían poder eliminar el código de las características del marco no utilizadas en el momento de la compilación, también conocido como "Tree Shaking", y solo empaquetar el código que usan. Esto también nos permitirá publicar funcionalidades que algunos usuarios encontrarán útiles, sin incrementar el costo efectivo de descarga para el resto de usuarios.

En Vue 3, logramos este objetivo al mover la mayoría de las API globales y los ayudantes internos a las exportaciones de módulos ES. Esto permite que las herramientas de empaquetado modernas analicen estáticamente las dependencias de los módulos y eliminen el código relacionado con las exportaciones no utilizadas. El compilador de plantillas también genera código compatible con Tree Shaking. Si la función se usa realmente en la plantilla, el código solo importa el ayudante para la función.

Ciertas partes del marco nunca serán Tree Shaking, porque son esenciales para cualquier tipo de aplicación. A las métricas de estas piezas indispensables las llamamos tamaño base. A pesar de la adición de muchas características nuevas, el tamaño base de Vue 3 después de gzip es de solo 10 KB, que es incluso menos de la mitad de Vue 2.

Satisfacer las necesidades de escala

También queremos mejorar la capacidad de Vue para manejar aplicaciones grandes. Nuestro diseño inicial de Vue se centró en una curva de aprendizaje suave. Pero a medida que Vue se fue adoptando cada vez más, aprendimos más sobre las necesidades de los proyectos, que contenían cientos de módulos y fueron mantenidos por docenas de desarrolladores a lo largo del tiempo. Para este tipo de proyectos, un sistema de tipos como TypeScript y la capacidad de código reutilizable son cruciales, y el soporte de Vue 2 en estas áreas no es ideal.

En las primeras etapas del diseño de Vue 3, intentamos mejorar la integración de TypeScript proporcionando soporte incorporado para escribir componentes usando clases. El desafío es que muchas de las características del lenguaje (como los campos de clase y los decoradores) que necesitamos para que las clases estén disponibles siguen siendo propuestas y pueden cambiar antes de que se conviertan formalmente en parte de JavaScript. La complejidad y la incertidumbre involucradas nos hacen dudar si la adición de Class API es realmente razonable, porque no proporciona ninguna otra característica además de proporcionar una mejor integración con TypeScript.

Decidimos estudiar otras formas de resolver el problema de la expansión. Inspirándonos en React Hooks, consideramos exponer las vistas basadas en datos de nivel inferior y las API del ciclo de vida de los componentes para lograr una forma más libre de escribir la lógica de los componentes, denominada API de composición. No es necesario especificar una larga lista de opciones para definir componentes. La API de composición permite a los usuarios expresar, escribir y reutilizar la lógica de componentes con estado tan libremente como las funciones de escritura, al tiempo que proporciona un excelente soporte para TypeScript.

Estamos muy entusiasmados con esta idea. Aunque la API de composición está diseñada para resolver categorías específicas de problemas, técnicamente hablando, solo se puede usar al escribir componentes. En el primer borrador de la propuesta, insinuamos que podríamos reemplazar la API de opciones existente con la API de composición en una versión futura. Esto ha generado mucha insatisfacción entre los miembros de la comunidad, lo que nos enseñó una valiosa lección, permitiéndoles comunicar claramente sus planes e intenciones a largo plazo, así como comprender las necesidades de los usuarios. Después de escuchar los comentarios de nuestra comunidad, rediseñamos completamente la propuesta para dejar en claro que la API de composición será un complemento y una API complementaria a las opciones. La propuesta revisada fue más activa y se recibieron muchas sugerencias constructivas.

Busca el equilibrio

Entre los más de un millón de desarrolladores de Vue, hay principiantes que solo conocen los conceptos básicos de HTML y CSS, programadores antiguos que migraron de jQuery y el front-end migraron desde otro marco, y algunos incluso están buscando una solución de front-end. El ingeniero de back-end de la solución y el arquitecto de software del software de procesamiento a gran escala. Algunos desarrolladores pueden querer agregar interactividad a aplicaciones más antiguas, mientras que otros pueden requerir un desarrollo ágil pero proyectos únicos con requisitos de mantenimiento limitados. A lo largo del ciclo de vida del proyecto, es posible que deba lidiar con proyectos grandes de varios años y un equipo de desarrollo volátil.

Si bien buscamos equilibrar varios compromisos, el diseño de Vue está constantemente influenciado e inspirado por estas necesidades. Vue se conoce como el "marco progresivo", que encapsula el diseño de API jerárquico resultante de este proceso. Los principiantes pueden usar scripts CDN, plantillas basadas en HTML y API de opciones intuitivas para aprender fácilmente, mientras que los maestros pueden usar la CLI con todas las funciones, las funciones de renderizado y la API de composición para manejar proyectos más complejos.

Todavía queda mucho trabajo por hacer para hacer realidad nuestra visión. Lo más importante es actualizar las bibliotecas, los documentos y las herramientas circundantes para garantizar una migración sin problemas. En los próximos meses, continuaremos trabajando duro y estamos ansiosos por ver qué creará la comunidad con Vue 3.

Supongo que te gusta

Origin blog.csdn.net/GetIdea/article/details/106405464
Recomendado
Clasificación