Vue.js: Composables vs. Mixins – Reutilize código com eficiência!

Na programação, é fundamental organizar seu código de maneira que permita a reutilização de componentes sempre que possível. A duplicação de código pode sobrecarregar o sistema e dificultar a depuração, principalmente em aplicações maiores.

O Vue facilita a reutilização de código por meio de “composables”. Composables são funções que agrupam a lógica, permitindo que você as empregue em todo o projeto para lidar com funcionalidades semelhantes.

A reutilização de código sempre foi feita com “composables”?

Antes do Vue 3 introduzir os “composables”, os mixins eram usados para capturar trechos de código e reutilizá-los em diversas partes da aplicação. Mixins continham opções do Vue.js, como dados, métodos e ganchos de ciclo de vida, permitindo a reutilização de código entre componentes.

Para criar mixins, você os organizava em arquivos separados e os aplicava aos componentes adicionando-os à propriedade `mixins` dentro do objeto de opções do componente. Por exemplo:

export const formValidationMixin = {
    data() {
        return {
            formData: {
                username: '',
                password: '',
            },
            formErrors: {
                username: '',
                password: '',
            },
        };
    },
    methods: {
        validateForm() {
            this.formErrors = {};

            if (!this.formData.username.trim()) {
                this.formErrors.username="Username is required.";
            }

            if (!this.formData.password.trim()) {
                this.formErrors.password = 'Password is required.';
            }

            return Object.keys(this.formErrors).length === 0;
        },
    },
};

Este código mostra um mixin para validação de formulários. Ele contém duas propriedades de dados – `formData` e `formErrors` – inicialmente definidas com valores vazios.

`formData` armazena os dados de entrada do formulário, incluindo os campos de nome de usuário e senha, inicialmente vazios. `formErrors` espelha essa estrutura para conter mensagens de erro, também inicialmente vazias.

O mixin também possui um método, `validateForm()`, que verifica se os campos de nome de usuário e senha não estão vazios. Se um dos campos estiver vazio, ele preenche a propriedade de dados `formErrors` com a mensagem de erro adequada.

O método retorna `true` para um formulário válido, quando `formErrors` está vazio. Você pode usar o mixin importando-o para seu componente Vue e adicionando-o à propriedade `mixin` do objeto Options:

<template>
    <div>
        <form @submit.prevent="submitForm">
            <div>
                <label for="username">Username:</label>
                <input type="text" id="username" v-model="formData.username" />
                <span class="error">{{ formErrors.username }}</span>
            </div>
            <div>
                <label for="password">Password:</label>
                <input type="password" id="password" v-model="formData.password" />
                <span class="error">{{ formErrors.password }}</span>
            </div>
            <button type="submit">Submit</button>
        </form>
    </div>
</template>

<script>
import { formValidation } from "./formValidation.js";

export default {
    mixins: [formValidation],
    methods: {
        submitForm() {
            if (this.validateForm()) {
                alert("Form submitted successfully!");
            } else {
                alert("Please correct the errors in the form.");
            }
        },
    },
};
</script>

<style>
.error {
    color: red;
}
</style>

Este exemplo mostra um componente Vue usando a abordagem de objeto Options. A propriedade `mixins` inclui todos os mixins que você importou. Nesse caso, o componente usa o método `validateForm` do mixin `formValidation` para informar ao usuário se o envio do formulário foi bem-sucedido.

Como utilizar “composables”

Um “composable” é um arquivo JavaScript independente com funções adaptadas a necessidades específicas. Você pode aproveitar a API de Composição do Vue em um “composable”, usando recursos como `refs` e `computed refs`.

Este acesso à API de Composição permite criar funções que se integram em vários componentes. Essas funções retornam um objeto que você pode importar e utilizar facilmente nos componentes Vue através da função `setup` da API de Composição.

Crie um novo arquivo JavaScript no diretório `src` do seu projeto para usar um “composable”. Para projetos maiores, considere organizar uma pasta dentro de `src` e criar arquivos JavaScript separados para diferentes “composables”, garantindo que o nome de cada um reflita sua finalidade.

Dentro do arquivo JavaScript, defina a função necessária. Aqui está uma reestruturação do mixin `formValidation` como um “composable”:

import { reactive } from 'vue';

export function useFormValidation() {
    const state = reactive({
        formData: {
            username: '',
            password: '',
        },
        formErrors: {
            username: '',
            password: '',
        },
    });

    function validateForm() {
        state.formErrors = {};

        if (!state.formData.username.trim()) {
            state.formErrors.username="Username is required.";
        }

        if (!state.formData.password.trim()) {
            state.formErrors.password = 'Password is required.';
        }

        return Object.keys(state.formErrors).length === 0;
    }

    return {
        state,
        validateForm,
    };
}

Este código começa importando a função `reactive` do pacote `vue`. Em seguida, cria uma função exportável, `useFormValidation()`.

Continua criando uma variável reativa, `state`, que abriga as propriedades `formData` e `formErrors`. O código então lida com a validação do formulário de maneira muito similar ao mixin. Por fim, retorna a variável de estado e a função `validateForm` como um objeto.

Você pode usar este “composable” importando a função JavaScript do arquivo para seu componente:

<template>
    <div>
        <form @submit.prevent="submitForm">
            <div>
                <label for="username">Username:</label>
                <input type="text" id="username" v-model="state.formData.username" />
                <span class="error">{{ state.formErrors.username }}</span>
            </div>
            <div>
                <label for="password">Password:</label>
                <input type="password" id="password" v-model="state.formData.password" />
                <span class="error">{{ state.formErrors.password }}</span>
            </div>
            <button type="submit">Submit</button>
        </form>
    </div>
</template>

<script setup>
import { useFormValidation } from "./formValidation.js";
import { ref } from "vue";
const { state, validateForm } = useFormValidation();

const submitForm = () => {
    if (validateForm()) {
        alert("Form submitted successfully!");
    } else {
        alert("Please correct the errors in the form.");
    }
};
</script>

<style>
.error {
    color: red;
}
</style>

Após importar o “composable” `useFormValidation`, este código desestrutura o objeto JavaScript que ele retorna e continua com a validação do formulário. Ele alerta se o formulário enviado foi bem-sucedido ou contém erros.

“Composables” são os novos mixins

Embora os mixins fossem úteis no Vue 2 para reutilização de código, os “composables” os substituíram no Vue 3. Os “composables” fornecem uma abordagem mais estruturada e sustentável para reutilizar a lógica em aplicativos Vue.js, facilitando a construção de aplicações web escaláveis com o Vue.