# Composition API

https://v3.vuejs.org/guide/composition-api-introduction.html#why-composition-api (opens new window)

Es una alternativa que podrá solucionar diferentes problemas de Vue 2:

  • Legibilidad cuando nuestros componentes son muy grandes
  • Reutilización de código tiene sus inconvenientes
  • Soporte limitado para TypeScript

# Cuando utilizarla

  • Cuando necesitas una compatibilidad al 100% de TypeScript.
  • El componente es demasiado grande y necesitas organizar por función.
  • Necesitas reutilizar código de otros componentes.

# API de opciones

Disponible para Vue 2 como Vue 3

<template>
  <div class="home">
    <h1
      :style="{'color': color}"
    >
      Contador: {{contador}}
    </h1>
    <button @click="aumentar">+</button>
    <button @click="disminuir">-</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      contador: 0
    }
  },
  methods: {
    aumentar(){
      this.contador ++
    },
    disminuir(){
      this.contador --
    }
  },
  computed: {
    color(){
      if(this.contador < 0) {
        return 'red'
      }else{
        return 'blue'
      }
    }
  }
}
</script>

# setup()

La setup se ejecuta antes de que se cree el componente, una vez que props se resuelven, y sirve como punto de entrada para las API de composición.

<template>
  <div class="about">
    <h1>
      contador {{contador}}
    </h1>
  </div>
</template>

<script>
export default {
  setup() {
    const contador = 0;
    return {contador}
  }
};
</script>

# ref

Es una referencia reactiva, en nuestro ejemplo necesitamos un entero que sea "rastreable", por ende utilizamos ref. Por ende ahora contador es reactivo.

ref toma el argumento y lo devuelve envuelto dentro de un objeto con una value propiedad, que luego puede usarse para acceder o mutar el valor de la variable reactiva

<script>
import { ref } from 'vue';
export default {
  setup() {
    const contador = ref(0)
    console.log(contador.value)
    return {contador}
  }
};
</script>

# Methods

<template>
  <div class="about">
    <h1>
      contador {{contador}}
    </h1>
    <button @click="aumentar">+</button>
    <button @click="disminuir">-</button>
  </div>
</template>

<script>
import { ref } from 'vue'
export default {
  setup(){
    const contador = ref(0)

    const aumentar = () => {
      contador.value ++
    }

    const disminuir = () => {
      contador.value --
    }
    
    return {contador, aumentar, disminuir}
  }
}
</script>

# Computed

<template>
  <div class="about">
    <h1
      :style="{'color': color}"
    >
      contador {{contador}}
    </h1>
    <button @click="aumentar">+</button>
    <button @click="disminuir">-</button>
  </div>
</template>

<script>
import { computed, ref } from 'vue'
export default {
  setup(){
    const contador = ref(0)

    const aumentar = () => {
      contador.value ++
    }

    const disminuir = () => {
      contador.value --
    }

    const color = computed(() => {
      if(contador.value < 0){
        return 'red'
      }else{
        return 'blue'
      }
    })
    
    return {contador, aumentar, disminuir, color}
  }
}
</script>

# v-model

<template>
  <div class="about">
    <h1
      :style="{'color': color}"
    >
      contador {{contador}}
    </h1>
    <button @click="aumentar">+</button>
    <button @click="disminuir">-</button>
    
    <hr>
    <input type="text" v-model="texto">
    <p>{{texto}}</p>
  </div>
</template>

<script>
import { computed, ref } from 'vue'
export default {
  setup(){
    const contador = ref(0)
    const texto = ref('')

    const aumentar = () => {
      contador.value ++
    }

    const disminuir = () => {
      contador.value --
    }

    const color = computed(() => {
      if(contador.value < 0){
        return 'red'
      }else{
        return 'blue'
      }
    })
    
    return {contador, aumentar, disminuir, color, texto}
  }
}
</script>

# props

components/Titulo.vue

<template>
    <h1
      :style="{'color': color}"
    >
      contador {{signoPeso}}
    </h1>
</template>

<script>
import { computed } from 'vue'
export default {
    props: ['color', 'contador'],
    
    setup(props){
        const signoPeso = computed(() => {
            return '$ ' + props.contador
        })

        return {signoPeso}
    
    }
}
</script>

About.vue

<template>
  <div class="about">

    <Titulo :contador="contador" :color="color" />
    ...
  </div>
</template>

<script>
import Titulo from '../components/Titulo'
import { computed, ref } from 'vue'
export default {
  components: {
    Titulo
  },
  setup(){
    
    ...
    
    return {contador, aumentar, disminuir, color, texto}
  }
}
</script>

# Custom Events

https://v3.vuejs.org/guide/composition-api-setup.html#arguments (opens new window)

<Btn :textoBoton="'Aumentar'" @accion="aumentar" />
<Btn :textoBoton="'Disminuir'" @accion="disminuir" />

Btn.vue

<template>
<!-- <button @click="$emit('accion')">{{textoBoton}}</button> -->
  <button @click="accionBoton">{{textoBoton}}</button>
</template>

<script>
export default {
    props: ['textoBoton'],
    setup(props, context){

        const accionBoton = () => {
            context.emit('accion')
        }

        return {accionBoton}
    }
}
</script>

# Reutilización

Llamados como Hooks o Composables.

contadorHook.js

import { ref } from 'vue'
export function contadorHook() {
    const contador = ref(0)

    const aumentar = () => {
      contador.value ++
    }

    const disminuir = () => {
      contador.value --
    }
    
    return {contador, aumentar, disminuir}
}

Contador.vue

<template>
  <div class="about">
    <h1>Contador {{contador}}</h1>
    <button @click="aumentar">+</button>
    <button @click="disminuir">-</button>
  </div>
</template>

<script>
import {contadorHook} from '../hooks/contadorHook'
export default {
    setup(){
        // console.log(contadorHook())
        return {...contadorHook()}
    }
}
</script>

# Export vs Export Default

Puede que confunda a veces que opción utilizar cuando reutilizamos nuestras funciones, las dos son válidas y aquí trato de explicar sus dos implementaciones:

  • export: Se pueden exportar más de una función por archivo, por ende al momento de llamar (importar) tenemos que utilizar {} y el nombre utilizado, en este caso sería {contadorHook}
import { ref } from 'vue'
export function contadorHook() {
    ...   
    return {contador, aumentar, disminuir}
}
import {contadorHook} from '../hooks/contadorHook'
  • export default: Solo permite una exportación por archivo y puede recibir un nombre o no la función, como vemos a continuación, pero lo importante es que al momento de importar ya no utilizamos las {}
import { ref } from 'vue'

// el nombre contadorHook es opcional -> se podría omitir:
// export default function () {
export default function contadorHook() {
    ...   
    return {contador, aumentar, disminuir}
}
import contadorHook from '../hooks/contadorHook'

# Ciclo de vida de Vue

https://v3.vuejs.org/guide/composition-api-lifecycle-hooks.html (opens new window) Los enlaces de ciclo de vida en la API de composición tienen el mismo nombre que la API de opciones, pero tienen el prefijo on:

mounted => onMounted

<script>
import {
  onBeforeMount,
  onMounted,
  onBeforeUpdate,
  onUpdated,
  onBeforeUnmount,
  onUnmounted,
} from "vue";

export default {
  setup() {
    onBeforeMount(() => {
      console.log("onBeforeMount");
    });
    onMounted(() => {
      console.log("onMounted");
    });
    onBeforeUpdate(() => {
      console.log("onBeforeUpdate");
    });
    onUpdated(() => {
      console.log("onUpdated");
    });
    onBeforeUnmount(() => {
      console.log("onBeforeUnmount");
    });
    onUnmounted(() => {
      console.log("onUnmounted");
    });
  }
};
</script>

Como vemos faltan "beforeCreate" y "created" los cuales no son necesarios en la API de composición.

  • beforeCreate: se llama justo antes del setup()
  • created: después del setup()

Por lo tanto el llamado a las apis irian dentro del setup() sin necesidad de un ciclo de vida en particular.

# Consumir API

https://restcountries.eu/ (opens new window)

Paises.vue

<template>
  <h1>API País</h1>
  <p v-for="(pais, index) in arrayData" :key="index">
      {{pais.name}}
  </p>
</template>

<script>
import { ref } from 'vue'
export default {
    setup(){
        const arrayData = ref([])
        
        onMounted(async() => {    
            try {
                const res = await fetch('api.json')
                arrayData.value = await res.json()
            } catch (error) {
                console.log(error)
            }
        })
        
        return {arrayData}
    }
}
</script>

Router




 



{
  path: '/paises/:nombre',
  name: 'Pais',
  props: true,
  component: () => import(/* webpackChunkName: "Paises" */ '../views/Pais.vue')
}

Router-link

<template>
  <h1>API País</h1>
  <p v-for="(pais, index) in arrayData" :key="index">
      <router-link :to="`/paises/${pais.name}`">
        {{pais.name}}
      </router-link>
  </p>
</template>

Hook/fetchData.js

import { onMounted, ref } from 'vue'
export function fetchData(url) {
    const arrayData = ref([])
        
    onMounted(async() => {    
        try {
            const res = await fetch(url)
            arrayData.value = await res.json()
        } catch (error) {
            console.log(error)
        }
    })
    
    return {arrayData}
}

Paises.vue

<template>
  <h1>API País</h1>
  <p v-for="(pais, index) in arrayData" :key="index">
      <router-link :to="`/paises/${pais.name}`">
        {{pais.name}}
      </router-link>
  </p>
</template>

<script>
import {fetchData} from '../hooks/fetchData'
export default {
    setup(){
        return {...fetchData('api.json')}
    }
}
</script>

Pais.vue

<template>
  <h1>Detalle País</h1>
  {{$route.params.nombre}}
  {{nombre}}
  <p v-for="(pais, index) in arrayData" :key="index">  
    {{pais.name}} - {{pais.region}} <br>
    {{pais.population}}
  </p>
</template>

<script>
import {fetchData} from '../hooks/fetchData'
export default {
    props: ['nombre'],
    setup(props){
        console.log(props.nombre)
        const {arrayData} = fetchData(`https://restcountries.eu/rest/v2/name/${props.nombre}`)
        return {arrayData}
    }
}
</script>

# useRoute

<script>
import {fetchData} from '../hooks/fetchData'
import {useRoute} from 'vue-router'
export default {
    props: ['nombre'],
    setup(props){

        const route = useRoute();
        console.log(route.params.nombre)

        const {arrayData} = fetchData(`https://restcountries.eu/rest/v2/name/${route.params.nombre}`)
        
        return {arrayData}
    }
}
</script>

# Vue Router

404 https://www.vuemastery.com/blog/vue-router-a-tutorial-for-vue-3/ (opens new window)

Last Updated: 12/3/2022, 9:51:20