Saltar al contenido

El primer caso va a ser simplemente un hola mundo para pasar un componente sencillo de Vue 2 a Vue 3 utilizando la nueva api de composición que Vue 3 nos ofrece.

hola-mundo-vue-2.js

Vue.component('hola-mundo', {
  data () {
    return {
      message: 'Hola Mundo'
    }
  },
  template: `
    <div>
        <h2>Componente Hola Mundo en Vue 2</h2>
        <p>{{ message }}</p>
    </div>
  `
});

hola-mundo-vue-3.js

app.component('hola-mundo', {
    setup() {
        const message = ref("Hola Mundo")
        return { message }
    },
    template: `
        <h2>Componente Hola Mundo en Vue 3</h2>
        <p>{{ message }}</p>
    `
});

Computed Properties

Ahora vamos a ver cómo pasar un componente en Vue 2 que utilice computed properties a Vue 3 con la api de composición.

computed-properties-vue-2.js

Vue.component('computed-properties', {
    data () {
      return {
        name: 'Israel',
        surname: 'Parra'
      }
    },
    computed: {
      fullName () {
        return `${this.name} ${this.surname}`;
      }
    },
    template: `
      <div>
        <h2>Computed properties</h2>
        <p>{{ fullName }}</p>
      </div>
    `
});

computed-properties-vue-3.js

app.component('computed-properties', {
    setup() {
        const name = ref("Israel");
        const surname = ref("Parra");
        const fullName = computed(() => {
            return `${name.value} ${surname.value}`;
        });
        return { name, surname, fullName };
    },
    template: `
        <h2>Computed properties en Vue 3</h2>
        <p>Nombre: {{ name }} | Apellido: {{ surname }}</p>
        <p>Nombre completo: {{ fullName }}</p>
    `
});

Methods

Ahora vamos a ver cómo pasar un componente en Vue 2 que utillice methods a Vue 3 con la api de composición.

methods-vue-2.js

Vue.component('methods', {
  data () {
    return {
      name: 'Israel',
      surname: 'Parra'
    }
  },
  computed: {
    fullName () {
      return `${this.name} ${this.surname}`;
    }
  },
  methods: {
    helloUser () {
      alert(this.fullName);
    }
  },
  template: `
    <div>
        <h2>Ejecutar métodos con Vue 2</h2>
        <p @click="helloUser">Pulsa aquí para ejecutar el método helloUser</p>
    </div>
  `
});

methods-vue-3.js

app.component('methods', {
    setup() {
        const name = ref("Israel");
        const surname = ref("Parra");
        const fullName = computed(() => {
            return `${name.value} ${surname.value}`;
        });
        const helloUser = () => {
            alert(`Hola ${fullName.value}`);
        }
        return { helloUser };
    },
    template: `
        <h2>Ejecutar métodos con Vue 3</h2>
        <p @click="helloUser">Pulsa aquí para ejecutar el método helloUser</p>
    `
});

V-MODEL básico

La directiva v-model ha sido mejorada considerablemente, en esta ocasión simplemente vamos a ver un sencillo ejemplo de uso haciendo uso del método setup de Vue 3.

v-model-vue-2.js

Vue.component('vmodel', {
    data () {
        return {
          framework: 'Vue 2'
        }
    },
    template: `
      <div>
        <h2>Trabajando con v-model en Vue 2</h2>
        <input v-model="framework" />
        <p>El framework escogido es: {{ framework }}</p>
      </div>
    `
})

v-model-vue-3.js

app.component('vmodel', {
    setup() {
        const framework = ref("Vue 3");
        return { framework };
    },
    template: `
        <h2>Trabajando con v-model en Vue 3</h2>
        <input v-model="framework" />
        <p>El framework escogido es: {{ framework }}</p>
    `
});

V-MODEL con Arrays

El trabajo con v-model utilizando Arrays es igual al v-model con un String, en este caso la diferencia es modificar el tipo de dato por un Array de la siguiente forma.

v-model-arrays-vue-2.js

Vue.component('vmodel-arrays', {
    data () {
      return {
        frameworks: []
      }
    },
    template: `
      <div>
        <h2>vmodel con arrays en Vue 2</h2>
        <input type="checkbox" id="vue" value="Vue" v-model="frameworks" />
        <label for="vuejs2">Vue</label>
        <input type="checkbox" id="laravel" value="Laravel" v-model="frameworks" />
        <label for="reactjs">Laravel</label>
        <input type="checkbox" id="django" value="Django" v-model="frameworks" />
        <label for="django">Django</label>
        <p>Frameworks seleccionados: {{ frameworks }}</p>
      </div>
    `
})

v-model-arrays-vue-3.js

app.component("vmodel-arrays", {
    setup() {
        const frameworks = ref([]);
        return { frameworks };
    },
    template: `
        <h2>v-model con arrays en Vue 3</h2>
        <input type="checkbox" id="vue" value="Vue" v-model="frameworks" />
        <label for="vuejs2">Vue</label>
        <input type="checkbox" id="laravel" value="Laravel" v-model="frameworks" />
        <label for="reactjs">Laravel</label>
        <input type="checkbox" id="django" value="Django" v-model="frameworks" />
        <label for="django">Django</label>
        <p>Frameworks seleccionados: {{ frameworks }}</p>
    `
})

Comunicación entre componentes padre/hijo

La comunicación parent/child entre componentes en Vue 3 si que ha sufrido más cambios, veamos un ejemplo en Vue 2 y Vue 3.

parent-child-vue-2.js

Vue.component('child', {
    data () {
      return {
        carBrand: 'Hyundai'
      }
    },
    methods: {
        emitShowCarBrand() {
            this.$emit("showCarBrand", this.carBrand)
        }
    },
    template: `
      <div>
        <h2>Emitir eventos con Vue 2</h2>
        <p>{{ $parent.parentData }}</p>
        <p @click="$emit('showCarBrand', carBrand)">
            Emitir un evento al componente Parent desde la template
        </p>
        <p @click="emitShowCarBrand">
            Emitir un evento al componente Parent desde el setup
        </p>
      </div>
    `
});

Vue.component('parent', {
    data() {
        return {
            parentData: "Datos del componente padre",
        }
    },
    methods: {
        eventReceived(cardBrand) {
            console.log(cardBrand);
        }
    },
    template: `
      <div>
        <h2>Comunicación a través de eventos padre hijo en Vue 3</h2>
        <child @showCarBrand="eventReceived"></child>
      </div>
    `
})

parent-child-vue-3.js

app.component("child", {
    emits: ['showCarBrand'],
    setup(props, { emit }) {
        const carBrand = ref("Hyundai");
        const emitShowCarBrand = () => {
            emit("showCarBrand", carBrand.value)
        }
        return { carBrand, emitShowCarBrand };
    },
    template: `
        <h2>Emitir eventos con Vue 3</h2>
        <u>{{ $parent.parentData }}</u>
        <p @click="$emit('showCarBrand', carBrand)">
            Emitir un evento al componente Parent desde la template
        </p>
        <p @click="emitShowCarBrand">
            Emitir un evento al componente Parent desde el setup
        </p>
    `
})

app.component('parent', {
    setup() {
        const parentData = ref("Datos del componente padre");
        const eventReceived = (cardBrand) => {
            console.log(cardBrand);
        }
        return { parentData, eventReceived };
    },
    template: `
        <h2>Comunicación a través de eventos padre hijo en Vue 3</h2>
        <child @showCarBrand="eventReceived"></child>
    `
})

Refs en templates

Un tema que también ha sufrido algunos cambios es la forma de utilizar refs en las templates, ya que si bien en Vue 2 únicamente teníamos refs en las templates y accedíamos desde los componentes con this.$refs, ahora también podemos definir datos haciendo uso de ref con los tipos primitivos más comunes.

refs-vue-2.js

Vue.component('refs', {
    mounted() {
        console.log(this.$refs.childRef.carBrand); // Hyundai
    },
    data() {
        return {
            parentData: "Datos del componente padre",
        }
    },
    template: `
        <div>
            <h2>Refs en Vue 2</h2>
            <child ref="childRef"></child>
        </div>
    `
})

refs-vue-3.js

app.component('refs', {
    setup() {
        const childRef = ref(null);
        const parentData = ref("Datos del componente padre");
        onMounted(() => {
            console.log(childRef.value.carBrand); // Hyundai
        })
        return { childRef, parentData };
    },
    template: `
        <h2>Refs en Vue 3</h2>
        <child ref="childRef"></child>
    `
})

Obtener datos externos, ejemplo con Fetch

El acceso a datos remotos de forma asíncrona también ha cambiado y ahora debemos modificar nuestra forma de trabajar.

fetch-vue-2.js

Vue.component('fetch', {
    data () {
      return {
        users: []
      }
    },
    async mounted() {
        const response = await fetch('https://jsonplaceholder.typicode.com/users');
        this.users = await response.json()
      }
    },
    template: `
      <div>
         <h2>Fetch con Vue 2</h2>
         <ul v-if="users.length">
            <li v-for="user in users" :key="user.id">
                {{ user.name }} - {{ user.email }}
            </li>
         </ul>
         <p v-else>No hay usuarios disponibles</p>
      </div>
    `
})

fetch-vue-3.js

app.component("fetch", {
    setup () {
        const users = ref([]);
        onMounted(async () => {
            const response = await fetch("https://jsonplaceholder.typicode.com/users");
            users.value = await response.json()
        });
        return { users };
    },
    template: `
         <h2>Fetch en Vue 3</h2>
         <ul v-if="users.length">
             <li v-for="user in users" :key="user.id">
                 {{ user.name }} - {{ user.email }}
             </li>
         </ul>
         <p v-else>No hay usuarios disponibles</p>
    `
});

Desarrollo de plugins

En Vue 2 la base para desarrollar un plugin es crear un objeto con el método install y utilizarlo con Vue.use. Esto en Vue 3 es posible, pero al utilizar el método setup perdemos el contexto del componente y debemos hacerlo de otra forma.

plugin-vue-2.js

const Profile = {
  install: (Vue, options) => {
    const { skills } = options;
    Vue.prototype.$profile = (name, age) => {
      return `Mi nombre es ${name}, tengo ${age} años y trabajo de ${skills}`;
    }
  }
};

 Vue.use(Profile, {
  skills: 'programador e instructor en Cursosdesarrolloweb'
});

plugin-vue-3.js

const createProfile = config => ({
    name: config.name,
    age: config.age,
    skills: config.skills,
    $skill(key) {
        return this.skills[key];
    }
});

const profileSymbol = Symbol();

function provideProfile(profileConfig) {
    const profile = createProfile(profileConfig);
    provide(profileSymbol, profile);
}

function useProfile() {
    const profile = inject(profileSymbol);
    if (!profile) throw new Error("Debes ejecutar provideProfile para utilizar este plugin");

    return profile;
}

Hasta aquí bien, pero ahora debemos saber cómo utilizar nuestro plugin en cada versión, veamos.

use-plugin-vue-2.js

Vue.component("use-plugin", {
  mounted() {
    console.log(this.$profile('Israel', 39));
    // Mi nombre es Israel, tengo 39 años y trabajo de programador e instructor en Cursosdesarrolloweb"
  }
});

use-plugin-vue-3.js

app.component("use-plugin", {
    setup() {
        provideProfile({
            name: "Israel",
            age: 39,
            skills: {
                programmer: "Programador PHP, JavaScript y Python",
                instructor: "Instructor de cursos en línea de desarrollo web y móvil en Cursosdesarrolloweb"
            }
        })
        const profile = useProfile();
        return { profile };
    },
    template: `
        <h2>Crear plugins en Vue 3</h2>
        <p>
            Nombre: {{ profile.name }}<br />
            Edad: {{ profile.age }}<br />
            Skills: {{ profile.$skill('programmer') }}
        </p>
    `
});

Filtros, desaparecen en Vue 3

Los filtros ya no existen en Vue 3, ahora simplemente podemos utilizar computed properties para formatear los datos a nuestras necesidades.

filters-vue-2.js

Vue.filter('currencyFilter', (value, currency) => {
  return `${value}${currency}`
});

Vue.component('filters', {
    data () {
      return {
        totalAmount: 9.99
      }
    },
    template: `
      <div>
        <h2>Filtros en Vue 2</h2>
        <input v-model="totalAmount" />
        <p>Cantidad total: {{ totalAmount | currencyFilter('€') }}</p>
      </div>
    `
});

filters-vue-3.js

app.component("filters", {
    setup() {
        const totalAmount = ref(9.99);
        const totalAmountFormatted = computed(() => {
            return `${totalAmount.value}€`;
        });
        return { totalAmountFormatted };
    },
    template: `
        <h2>Reemplazo de filtros en Vue 3</h2>
        <input v-model="totalAmount" />
        <p>Cantidad total: {{ totalAmountFormatted }}</p>
    `
})

Rutas de aprendizaje