9 consejos de rendimiento Vue

Este artículo tiene nueve consejos sobre cómo mejorar el rendimiento de su aplicación Vue, aumentar la velocidad de visualización y reducir el tamaño del paquete.

Trabajar con componentes


Componentes funcionales


Supongamos que tenemos un componente simple y pequeño. Todo lo que hace es mostrar una etiqueta particular dependiendo del valor pasado:

<template> <div> <div v-if="value"></div> <section v-else></section> </div> </template> <script> export default { props: ['value'] } </script> 

Este componente se puede optimizar agregando el atributo funcional . Un componente funcional se compila en una función simple y no tiene un estado local. Debido a esto, su rendimiento es mucho mayor:

 <template functional> <div> <div v-if="props.value"></div> <section v-else></section> </div> </template> <script> export default { props: ['value'] } </script> 

Un ejemplo de cómo se verían los componentes funcionales en Vue v3.0

Separación en componentes secundarios


Imagine un componente para mostrar que necesita para realizar una tarea compleja:

 <template> <div :style="{ opacity: number / 300 }"> <div>{{ heavy() }}</div> </div> </template> <script> export default { props: ['number'], methods: { heavy () { /*   */ } } } </script> 

El problema aquí es que Vue ejecutará el método heavy () cada vez que se vuelva a representar el componente, es decir, cada vez que cambie el valor de los accesorios.

Podemos optimizar fácilmente dicho componente si separamos el método pesado en un componente secundario:

 <template> <div :style="{ opacity: number / 300 }"> <ChildComp/> </div> </template> <script> export default { props: ['number'], components: { ChildComp: { methods: { heavy () { /*   */ } }, render (h) { return h('div', this.heavy()) } } } } </script> 

Por qué Vue omite automáticamente la representación de componentes en los que los datos dependientes no han cambiado. Por lo tanto, cuando se cambian los accesorios en el componente principal, el elemento secundario se reutilizará y el método heavy () no se reiniciará.

Tenga en cuenta que esto solo tiene sentido si el componente secundario no tiene dependencia de datos en el componente primario. De lo contrario, el hijo se volverá a crear junto con el padre, y entonces esta optimización no tiene sentido.

Getter Caché local


El siguiente componente tiene algún tipo de propiedad calculada basada en la segunda propiedad calculada:

 <template> <div :style="{ opacity: start / 300 }">{{ result }}</div> </template> <script> export default { props: ['start'], computed: { base () { return 42 }, result () { let result = this.start for (let i = 0; i < 1000; i++) { result += this.base } return result } } } </script> 

Lo importante aquí es que la propiedad base se llama en un bucle, lo que conduce a complicaciones. Cada vez que accede a datos reactivos, Vue ejecuta cierta lógica para determinar cómo y a qué datos está accediendo para construir dependencias, etc. Estos pequeños gastos generales se resumen si hay muchas llamadas, como en nuestro ejemplo.

Para solucionar esto, simplemente acceda a la base una vez y guarde el valor en una variable local:

 <template> <div :style="{ opacity: start / 300 }">{{ result }}</div> </template> <script> export default { props: ['start'], computed: { base () { return 42 }, result () { const base = this.base // <-- let result = this.start for (let i = 0; i < 1000; i++) { result += base } return result } } } </script> 

Reutilizando DOM con v-show


Eche un vistazo al siguiente ejemplo:

 <template functional> <div> <div v-if="props.value"> <Heavy :n="10000"/> </div> <section v-else> <Heavy :n="10000"/> </section> </div> </template> 

Aquí tenemos un componente contenedor que usa v-if y v-else para cambiar algunos componentes secundarios.

Es importante entender cómo funciona v-if. Cada vez, cuando se cambia un estado, un componente hijo se destruirá por completo (se llama al gancho destruido (), todos los nodos se eliminarán del DOM), y el segundo se creará completamente y se volverá a montar. Y si estos componentes son "pesados", obtendrá un bloqueo de la interfaz en el momento del cambio.

Solución más productiva: use v-show

 <template functional> <div> <div v-show="props.value"> <Heavy :n="10000"/> </div> <section v-show="!props.value"> <Heavy :n="10000"/> </section> </div> </template> 

En este caso, ambos componentes secundarios se crearán y montarán inmediatamente y existirán simultáneamente. Por lo tanto, Vue no necesita destruir y crear componentes al cambiar. Todo lo que hace es ocultar uno y mostrar el segundo con CSS. Por lo tanto, cambiar el estado será mucho más rápido, pero debe comprender que esto generará altos costos de memoria.

Use <keep-alive>


Entonces, un componente simple es un componente envoltorio sobre un enrutador .

 <template> <div id="app"> <router-view/> </div> </template> 

El problema es similar al ejemplo anterior: todos los componentes de su enrutador se crearán, montarán y destruirán durante las transiciones entre rutas.

Y la solución aquí es similar: decirle a Vue que no destruya sino que almacene en caché y reutilice los componentes. Puede hacerlo utilizando el componente especial integrado <keep-alive> :

 <template> <div id="app"> <keep-alive> <router-view/> </keep-alive> </div> </template> 

Esta optimización conducirá a un mayor consumo de memoria, ya que Vue admitirá más componentes "vivos". Por lo tanto, no debe usar este enfoque sin pensar en todas las rutas, especialmente si tiene muchas de ellas. Puede usar los atributos de inclusión y exclusión para establecer las reglas sobre qué rutas deben almacenarse en caché y cuáles no:

 <template> <div id="app"> <keep-alive include="home,some-popular-page"> <router-view/> </keep-alive> </div> </template> 

Renderizado diferido


El siguiente ejemplo es un componente que tiene varios componentes secundarios muy pesados:

 <template> <div> <h3>I'm an heavy page</h3> <Heavy v-for="n in 10" :key="n"/> <Heavy class="super-heavy" :n="9999999"/> </div> </template> 

El problema es que el procesamiento de componentes es una operación síncrona en el hilo principal. Y en este ejemplo, el navegador no mostrará nada hasta que finalice el procesamiento de todos los componentes, tanto primarios como secundarios. Y si lleva mucho tiempo procesar filiales, obtendrá retrasos en la interfaz o una pantalla en blanco durante algún tiempo.

Puede mejorar la situación retrasando la representación de los componentes secundarios:

 <template> <div> <h3>I'm an heavy page</h3> <template v-if="defer(2)"> <Heavy v-for="n in 10" :key="n"/> </template> <Heavy v-if="defer(3)" class="super-heavy" :n="9999999"/> </div> </template> <script> import Defer from '@/mixins/Defer' export default { mixins: [ Defer() ] } </script> 

Aquí, la función defer (n) devuelve n marcos falsos. después de lo cual siempre vuelve verdadero. Se usa para posponer el procesamiento de parte de la plantilla por varios cuadros, lo que le da al navegador la capacidad de dibujar la interfaz.

Como funciona Como escribí anteriormente, si la condición en la directiva v-if es falsa, entonces Vue ignora por completo parte de la plantilla.

En el primer cuadro de la animación obtenemos:

 <template> <div> <h3>I'm an heavy page</h3> <template v-if="false"> <Heavy v-for="n in 10" :key="n"/> </template> <Heavy v-if="false" class="super-heavy" :n="9999999"/> </div> </template> 

Vue simplemente monta el encabezado, y el navegador lo muestra y solicita un segundo marco. En este punto, diferir (2) comenzará a devolver verdadero

 <template> <div> <h3>I'm an heavy page</h3> <template v-if="true"> <Heavy v-for="n in 10" :key="n"/> </template> <Heavy v-if="false" class="super-heavy" :n="9999999"/> </div> </template> 

Vue creará 10 componentes secundarios y los montará. El navegador los mostrará y solicitará el siguiente marco, en el cual el aplazamiento (3) volverá verdadero.

Por lo tanto, creamos un componente pesado que se procesa gradualmente, dando al navegador la capacidad de mostrar las partes ya montadas de la plantilla, lo que mejorará en gran medida la experiencia de usuario ya que la interfaz no se verá como un bloqueo.

Código de aplazamiento del mezclador:

 export default function (count = 10) { return { data () { return { displayPriority: 0 } }, mounted () { this.runDisplayPriority() }, methods: { runDisplayPriority () { const step = () => { requestAnimationFrame(() => { this.displayPriority++ if (this.displayPriority < count) { step() } }) } step() }, defer (priority) { return this.displayPriority >= priority } } } } 

Carga diferida de componentes


Ahora hablemos sobre la importación de componentes secundarios:

 import Heavy from 'Heavy.js' export default { components: { Heavy } } 

En la importación tradicional, el componente secundario se carga inmediatamente, cuando el navegador llega a la declaración de importación. O, si usa el recopilador, su componente hijo se incluirá en el paquete general.

Sin embargo, si su componente hijo es muy grande, tiene sentido cargarlo de forma asíncrona.
Es muy fácil de hacer:

 const Heavy = () => import('Heavy.js') export default { components: { Heavy } } 

Eso es todo lo que necesitas . Vue puede trabajar con componentes de carga diferidos listos para usar. Descargará el componente él mismo cuando sea necesario y lo mostrará tan pronto como esté listo. Puede usar este enfoque para la carga diferida en cualquier lugar.

Si usa el recopilador, todo lo relacionado con Heavy.js se eliminará en un archivo separado. Por lo tanto, reducirá el peso de los archivos durante la descarga inicial y aumentará la velocidad de visualización.

Vistas de carga diferida


La carga diferida es muy útil en el caso de componentes utilizados para rutas. Como no necesita descargar todos los componentes de las rutas al mismo tiempo, le recomiendo que utilice siempre este enfoque:

 const Home = () => import('Home.js') const About = () => import('About.js') const Contacts = () => import('Contacts.js') new VueRouter({ routes: [ { path: '/', component: Home }, { path: '/about', component: About }, { path: '/contacts', component: Contacts }, ] }) 

Cada uno de estos componentes se cargará solo cuando el usuario solicite por primera vez la ruta especificada. Y no antes.

Componentes dinámicos


Del mismo modo, puede usar fácilmente la carga diferida con componentes dinámicos:

 <template> <div> <component :is="componentToShow"/> </div> </template> <script> export default { props: ['value'], computed: { componentToShow() { if (this.value) { return () => import('TrueComponent.js') } return () => import('FalseComponent.js') } } } </script> 

Nuevamente, cada componente solo se cargará cuando sea necesario.

Trabajar con Vuex


Evitar grandes compromisos


Imagine que necesita almacenar una gran variedad de datos en el almacenamiento:

 fetchItems ({ commit }, { items }) { commit('clearItems') commit('addItems', items) //   10000  } 

El problema es este. Todos los commits son operaciones sincrónicas. Esto significa que el procesamiento de una gran matriz bloqueará su interfaz durante la duración del trabajo.

Para resolver este problema, puede dividir la matriz en partes y agregarlas una a la vez, dando tiempo al navegador para dibujar un nuevo marco:

 fetchItems ({ commit }, { items, splitCount }) { commit('clearItems') const queue = new JobQueue() splitArray(items, splitCount).forEach( chunk => queue.addJob(done => { //       requestAnimationFrame(() => { commit('addItems', chunk) done() }) }) ) //      await queue.start() } 

Mientras más pequeños agreguen datos al almacenamiento, más suave será su interfaz y mayor será el tiempo total para completar la tarea.

Además, con este enfoque, tiene la capacidad de mostrar un indicador de descarga para el usuario. Lo que también mejorará significativamente su experiencia con la aplicación.

Apague la reactividad donde no se necesita


Y el último ejemplo de hoy. Tenemos una tarea similar: agregamos una matriz de objetos muy grandes al almacenamiento con un montón de niveles de anidación:

 const data = items.map( item => ({ id: uid++, data: item, // <--    vote: 0 }) ) 

El hecho es que Vue atravesará recursivamente todos los campos anidados y los hará reactivos. Si tiene muchos datos, esto puede ser costoso, pero lo que es mucho más importante: innecesario.

Si su aplicación está construida de tal manera que depende solo del objeto de nivel superior y no se refiere a datos reactivos en algún lugar a varios niveles por debajo, entonces puede desactivar la reactividad, ahorrando así a Vue de un montón de trabajo innecesario:

 const data = items.map( item => optimizeItem(item) ) function optimizeItem (item) { const itemData = { id: uid++, vote: 0 } Object.defineProperty(itemData, 'data', { //    "-" configurable: false, value: item }) return itemData } 

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


All Articles