Vue.Js 3, la Programación Orientada Al Futuro Que No Hay Que Perderse!

by Luigi Nori Date: 08-06-2020 vue hooks javascript react mixin


Si estás interesado en Vue.js, probablemente conozcas la 3ª versión de este framework, que se publicará en breve. La nueva versión de momento está en desarrollo, pero todas las características posibles se pueden encontrar en un repositorio RFC separado: https://github.com/vuejs/rfcs. Una de esas, function-api, puede cambiar dramáticamente el estilo de desarrollo de las aplicaciones Vue.

Este artículo está dirigido a personas que tienen al menos algún tipo de experiencia en JavaScript y Vue.

¿Qué tiene de malo la API actual de VUE 2?

La mejor manera es mostrarlo todo en un ejemplo. Por lo tanto, imaginemos que necesitamos implementar un componente que debería obtener los datos de algunos usuarios, mostrar el estado de carga y la barra superior dependiendo de la desviación de desplazamiento. Aquí está el resultado final:

Es una buena práctica extraer algo de lógica para reutilizarla en múltiples componentes. Con la API actual de Vue 2.x, hay una serie de patrones comunes, los más conocidos son:

  • Mixins (usando la opción mixins )
  • Componentes Higher-order (HOCs)

Por lo tanto, movamos la lógica de seguimiento de desplazamiento a un mixin, y busquemos la lógica en un componente de orden superior. La implementación típica con Vue se puede ver a continuación.

Scroll mixin:

 
const scrollMixin = {
data() {
return {
pageOffset: 0
}
},
mounted() {
window.addEventListener('scroll', this.update)
},
destroyed() {
window.removeEventListener('scroll', this.update)
},
methods: {
update() {
this.pageOffset = window.pageYOffset
}
}
}
 

Aquí añadimos la escucha de evento scroll (event listener), rastrear el desplazamiento de página y guardarlo en la propiedad pageOffset .

El componente de orden superior tendrá este aspecto::

 
import { fetchUserPosts } from '@/api'
const withPostsHOC = WrappedComponent => ({
props: WrappedComponent.props,
data() {
return {
postsIsLoading: false,
fetchedPosts: []
}
},
watch: {
id: {
handler: 'fetchPosts',
immediate: true
}
},
methods: {
async fetchPosts() {
this.postsIsLoading = true
this.fetchedPosts = await fetchUserPosts(this.id)
this.postsIsLoading = false
}
},
computed: {
postsCount() {
return this.fetchedPosts.length
}
},
render(h) {
return h(WrappedComponent, {
props: {
...this.$props,
isLoading: this.postsIsLoading,
posts: this.fetchedPosts,
count: this.postsCount
}
})
}
})
 

Aquí se inicializan las propiedades isLoading , posts para el estado de carga y posts data respectivamente. El método fetchPosts se invocará después de crear una instancia y cada vez que props.id cambia, para poder obtener datos para nuevas id .

No es una implementación completa de la COA, pero para este ejemplo, será suficiente. Aquí sólo tenemos que envolver el componente de destino y pasar las propriedades originales junto con las propriedades relacionadas con el fetch.

El componente de destino tiene este aspecto:

 
// ...
<script>
export default {
name: 'PostsPage',
mixins: [scrollMixin],
props: {
id: Number,
isLoading: Boolean,
posts: Array,
count: Number
}
}
</script>
// ...
 

Para obtener las propriedades especificadas debe ser envuelto en HOC creado:

 
_const_ PostsPage = **withPostsHOC(**PostsPage**)**
 

El componente completo con plantilla y estilos se puede encontrar en este enlace.

¡Genial! Acabamos de implementar nuestra tarea utilizando mixin y HOC, para que puedan ser utilizados por otros componentes. Pero no todo es tan prometedor, hay varios problemas con estos enfoques.

 

1. Choque de Namespace

 

Imagina que tenemos que añadir el método update a nuestro componente:

 
// ...
<script>
export default {
name: 'PostsPage',
mixins: [scrollMixin],
props: {
id: Number,
isLoading: Boolean,
posts: Array,
count: Number
},
methods: {
update() {
console.log('some update logic here')
}
}
}
</script>
// ...
 

Si vuelves a abrir la página y la desplaza, la barra superior ya no se mostrará. Esto se debe a la sobreescritura del método de mixin update . Lo mismo sucede con las COA. Si modificas el campo de datos fetchedPosts a posts :

 
const withPostsHOC = WrappedComponent => ({
props: WrappedComponent.props, // ['posts', ...]
data() {
return {
postsIsLoading: false,
posts: [] // fetchedPosts -> posts
}
},
// ...
 

…obtendrás errores como este:

La razón de esto es que el componente envuelto ya especificó la propiedad con el nombre posts .

 

2. Fuentes poco claras

 

¿Que pasa si después de un tiempo decidieras usar otro mixin en tu componente?

 
// ...
<script>
export default {
name: 'PostsPage',
mixins: [scrollMixin, mouseMixin],
// ...
 

¿Se puede saber exactamente de qué mixin se inyectó una propiedad pageOffset ? O en otro escenario, ambos mixins pueden tener, por ejemplo, la propiedad yOffset , por lo que el ultimo mixin anulará la propiedad de lo anterior. Eso no es bueno y puede causar muchos errores inesperados.

 

3. Rendimiento

 

Otro problema con las HOC es que necesitamos instancias de componentes separadas creadas sólo para fines de reutilización lógica que tienen un coste de rendimiento.

Vamos a hacer un “setup” de VUE 3

Veamos qué alternativa puede ofrecer la próxima versión de Vue.js y cómo podemos resolver el mismo problema usando la API basada en funciones.

Como Vue 3 aún no está disponible, se ha creado el plugin de ayuda — vue-function-api. Proporciona la función api de Vue3.x a Vue2.x para el desarrollo de aplicaciones Vue de nueva generación.

En primer lugar, es necesario instalarlo:

 
$ **npm** install vue-function-api
 

e instalar explícitamente a través de Vue.use() :

 
import Vue from 'vue'
import { plugin } from 'vue-function-api'
Vue.use(plugin)
 

La principal adición que proporciona la API basada en funciones es una nueva opción de componente - setup() . Como su nombre indica, este es el lugar donde utilizamos las funciones de la nueva API para configurar la lógica de nuestro componente. Por lo tanto, vamos a implementar una característica para mostrar la barra superior en función de la desviación de desplazamiento. Ejemplo de componente básico:

 
// ...
<script>
export default {
setup(props) {
const pageOffset = 0
return {
pageOffset
}
}
}
</script>
// ...
 

Ten en cuenta que la función de configuración setup recibe el objeto de puntales resueltos como su primer argumento y este objeto props es reactivo. También devolvemos un objeto que contiene la propiedad pageOffset para ser expuesto al contexto de render de la plantilla. Esta propiedad también se vuelve reactiva, pero sólo en el contexto del render. Podemos usarlo en la plantilla como de costumbre:

 
<div class="topbar" :class="{ open: pageOffset > 120 }">...</div>
 

Pero esta propiedad debería mutar en cada evento de desplazamiento. Para implementar esto, necesitamos agregar el listener de eventos de desplazamiento cuando el componente será montado y remover el listener - cuando sea desmontado. Para estos fines, existen funciones de API value , onMounted , onUnmounted :

 
// ...
<script>
import { value, onMounted, onUnmounted } from 'vue-function-api'
export default {
setup(props) {
const pageOffset = value(0)
const update = () => {
pageOffset.value = window.pageYOffset
}
onMounted(() => window.addEventListener('scroll', update))
onUnmounted(() => window.removeEventListener('scroll', update))
return {
pageOffset
}
}
}
</script>
// ...
 

Tenga en cuenta que todos los gancho(hooks)s de ciclo de vida en la versión 2.x de Vue tienen una función equivalente onXXX que puede ser usada dentro de setup() .

Probablemente también has notado que la variable pageOffset contiene una sola propiedad reactiva: .value . Necesitamos usar esta propiedad porque los valores primitivos en JavaScript como los números y las cadenas no se pasan por referencia. Las envolturas de valor proporcionan una forma de pasar referencias mutables y reactivas para tipos de valores arbitrarios.

Así es como se ve el objeto pageOffset :

El siguiente paso es implementar la obtención de datos del usuario. Al igual que cuando se utiliza la API basada en opciones, se pueden declarar valores calculados y observadores utilizando la API basada en funciones:

 
// ...
<script>
import {
value,
watch,
computed,
onMounted,
onUnmounted
} from 'vue-function-api'
import { fetchUserPosts } from '@/api'
export default {
setup(props) {
const pageOffset = value(0)
const isLoading = value(false)
const posts = value([])
const count = computed(() => posts.value.length)
const update = () => {
pageOffset.value = window.pageYOffset
}
onMounted(() => window.addEventListener('scroll', update))
onUnmounted(() => window.removeEventListener('scroll', update))
watch(
() => props.id,
async id => {
isLoading.value = true
posts.value = await fetchUserPosts(id)
isLoading.value = false
}
)
return {
isLoading,
pageOffset,
posts,
count
}
}
}
</script>
// ...
 

Un valor calculado se comporta como una propiedad calculada 2.x: rastrea sus dependencias y sólo reevalúa cuando éstas han cambiado. El primer argumento que se pasa a watch se llama "source", que puede ser uno de los siguientes:

  • una función getter
  • un valor wrapper
  • un array containing que contiene los dos tipos anteriores

El segundo argumento es una llamada de retorno que sólo será llamada cuando el valor devuelto por el getter o la envoltura de valor haya cambiado.

Acabamos de implementar el componente de destino utilizando una API basada en funciones.

El siguiente paso es hacer que toda esta lógica sea reutilizable..

Decomposición

Esta es la parte más interesante, para reutilizar código relacionado con un trozo de lógica sólo podemos extraerlo a lo que llamamos una “composition function” y devolverlo en estado reactivo:

 
// ...
<script>
import {
value,
watch,
computed,
onMounted,
onUnmounted
} from 'vue-function-api'
import { fetchUserPosts } from '@/api'
function useScroll() {
const pageOffset = value(0)
const update = () => {
pageOffset.value = window.pageYOffset
}
onMounted(() => window.addEventListener('scroll', update))
onUnmounted(() => window.removeEventListener('scroll', update))
return { pageOffset }
}
function useFetchPosts(props) {
const isLoading = value(false)
const posts = value([])
watch(
() => props.id,
async id => {
isLoading.value = true
posts.value = await fetchUserPosts(id)
isLoading.value = false
}
)
return { isLoading, posts }
}
export default {
props: {
id: Number
},
setup(props) {
const { isLoading, posts } = useFetchPosts(props)
const count = computed(() => posts.value.length)
return {
...useScroll(),
isLoading,
posts,
count
}
}
}
</script>
// ...
 

Observe cómo usamos useFetchPosts y useScroll para devolver las propiedades reactivas. Estas funciones pueden almacenarse en archivos separados y utilizarse en cualquier otro componente. En comparación con la solución basada en opciones:

  • Las propiedades expuestas a la plantilla tienen fuentes claras ya que son valores devueltos por las funciones de composición;
  • Devuelve los valores de las funciones de composición nombradas arbitrariamente para que no haya colisión de espacios de nombres;
  • No hay instancias de componentes innecesarias creadas sólo con fines de reutilización lógica.

Hay muchos otros beneficios que se pueden encontrar en la página oficial de RFC.

Todos los ejemplos de código utilizados en este artículo se pueden encontrar aquí.

Ejemplo en vivo del componente que puede ver aquí.

 

Conclusión

 

Como puedes ver, la API basada en funciones de Vue presenta una forma limpia y flexible de componer la lógica dentro y entre componentes sin ninguna de las desventajas de la API basada en opciones. Imagína lo poderosas que podrían ser las funciones de composición para cualquier tipo de proyecto, desde aplicaciones web pequeñas hasta grandes y complejas.

Espero que este post haya sido de utilidad.

 
by Luigi Nori Date: 08-06-2020 vue hooks javascript react mixin visitas : 8124  
 
Luigi Nori

Luigi Nori

He has been working on the Internet since 1994 (practically a mummy), specializing in Web technologies makes his customers happy by juggling large scale and high availability applications, php and js frameworks, web design, data exchange, security, e-commerce, database and server administration, ethical hacking. He happily lives with @salvietta150x40, in his (little) free time he tries to tame a little wild dwarf with a passion for stars.

 
 
 

Artículos relacionados

Modo oscuro en el sitio web usando CSS y JavaScript

En el artículo de hoy vamos a aprender a construir más o menos estándar en estos días en las páginas web y que es el modo de color alternativo y…

Javascript: los operadores Spread y Rest

En el artículo de hoy vamos a hablar de una de las características de la versión ES6 (ECMAScript 2015) de JavaScript que es el operador Spreadasí como el operador Rest. Estas…

Cookies HTTP: cómo funcionan y cómo usarlas

Hoy vamos a escribir sobre la forma de almacenar datos en un navegador, por qué los sitios web utilizan cookies y cómo funcionan en detalle. Continúa leyendo para averiguar cómo implementarlas…

Todas las funciones de javascript y los métodos para manipular los arrays

Este artículo mostrará las funciones prominentes de las arrays de JavaScript, entre ellas .map() , .filter() , y .reduce() , y luego pasará a través de ejemplos de casos en los que .every() …

Como construir mejores componentes de alto nivel (Higher-order components) con el Vue 3

El Vue 3 será lanzado pronto con la introducción del API de composición (Composition API). Viene con muchos cambios y mejoras en el rendimiento. Los componentes de orden superior (HOC…

Node.js: herramientas de código abierto para los desarrolladores

De la amplia gama de herramientas disponibles para simplificar el desarrollo de Node.js, aquí están las mejores. Una encuesta en StackOverflow afirma que el 49% de los desarrolladores usan Node.js para…

Renderización lado servidor de Vue.js on Php

¿Intentas que el renderizado del lado del servidor funcione con PHP para renderizar tu aplicación Vue.js pero se atasca? Hay un montón de grandes recursos por ahí, pero hasta ahra no…

Integración de Bootstrap 4 con Vue.js usando Bootstrap-Vue

React y Vue.js son dos marcos de trabajo líderes y modernos de JavaScript para el desarrollo del front-end. Mientras que React tiene una curva de aprendizaje considerable, y un proceso…

Cómo crear un aplicación Vue.js en 5 minutos

Vue.js se está volviendo cada vez más popular, convirtiéndose en un competidor significativo de frameworks como Angular o React.js. Como front-end framework para principiantes, conquista con éxito los corazones de…

Vue.js - Cómo usar (con seguridad) un plugin de jQuery con Vue.js

No es una buena idea usar jQuery y Vue.js en la misma interfaz. No lo hagas si puedes evitarlo, pero probablemente si estás leyendo esto no porque quieras usar jQuery…

Como construir una web application con Styled Components en Vue.js

Styled-Components es una biblioteca muy popular dentro de la comunidad React y React Native, y ahora puedes usarla en Vue.js. Para aquellos que no lo saben, Styled-Components es una biblioteca muy…

10 librerías para machine learning en JavaScript

JavaScript es actualmente uno de los lenguajes de programación más populares. Su principal aplicación se encuentra en las aplicaciones web, utilizándose para dar funcionalidad a las páginas web dinámicas. Otro…

Utilizamos cookies propias y de terceros para mejorar nuestros servicios, elaborar información estadística y analizar tus hábitos de navegación. Esto nos permite personalizar el contenido que ofrecemos y mostrarte publicidad relacionada con tus preferencias. Clicando en ‘Acepta todas’ aceptas el almacenamiento de cookies en tu dispositivo para mejorar la navegación en el sitio web, analizar el tráfico y ayudar en nuestras actividades de marketing. También puedes seleccionar ‘Sólo cookies de sistema’ para aceptar sólo las cookies necesarias para que la web funcione, o puedes seleccionar las cookies que quieres activar clicando en ‘Configuración’

Acepta todas Sólo cookies de sistema Configuración