Estrutura do Template e Fragments
No Vue 3, a introdução dos Fragments permite que um componente possua múltiplos nós raiz, eliminando a necessidade de envolver todo o conteúdo em uma div extra. Embora esta seja uma mudança estrutural importante, a sintaxe básica do template permanece bastante semelhante entre as versões. Nos exemplos a seguir, utilizaremos um nó raiz único para focar nas diferenças de lógica e gerenciamento de estado.
Gerenciamento de Estado e Dados
A distinção fundamental entre as duas versões é a mudança da Options API (Vue 2) para a Composition API (Vue 3). Na Options API, os dados são declarados dentro de um objeto retornado pela função data(). Na Composition API, utilizamos a função setup() e a API de reatividade para criar estados.
Abordagem Vue 2 (Options API):
export default {
props: {
formTitle: String
},
data() {
return {
taskName: '',
taskPriority: 'low'
}
}
}
Abordagem Vue 3 (Composition API):
É necessário importar reactive para agrupar as variáveis de estado e retorná-las através do setup().
import { reactive } from 'vue';
export default {
props: {
formTitle: String
},
setup() {
const state = reactive({
taskName: '',
taskPriority: 'low'
});
return { state };
}
}
Definição de Métodos
Na Options API, as funções são agrupadas na propriedade methods. Na Composition API, elas são declaradas como funções normais dentro do setup() e expostas via return.
Vue 2:
methods: {
submitTask() {
// Lógica de envio
}
}
Vue 3:
setup() {
// ... estado reativo
const submitTask = () => {
// Lógica de envio
};
return { state, submitTask };
}
Ciclo de Vida dos Componentes
Os hooks de ciclo de vida no Vue 2 são declarados como opções no nível raiz do componente. No Vue 3, eles devem ser importados explicitamente e chamados dentro da função setup().
Vue 2:
mounted() {
console.log('Componente montado com sucesso');
}
Vue 3:
import { onMounted } from 'vue';
setup() {
onMounted(() => {
console.log('Componente montado com sucesso');
});
}
Propriedades Computadas
As propriedades computadas seguem a mesma lógica de importação sob demanda no Vue 3, otimizando o tamanho final do pacote através de tree-shaking.
Vue 2:
computed: {
upperCaseTask() {
return this.taskName.toUpperCase();
}
}
Vue 3:
import { reactive, computed } from 'vue';
setup() {
const state = reactive({
taskName: '',
taskPriority: 'low',
upperCaseTask: computed(() => state.taskName.toUpperCase())
});
return { state };
}
Recepção de Props e Emissão de Eventos
No Vue 2, a instância do componente é acessada via this, permitindo a leitura de props e a emissão de eventos com this.$emit. No Vue 3, o contexto this não está disponível no setup(). Em vez disso, as props são recebidas como o primeiro argumento e o contexto (que contém emit) como o segundo.
Vue 2:
mounted() {
console.log(this.formTitle);
},
methods: {
submitTask() {
this.$emit('task-added', { name: this.taskName });
}
}
Vue 3:
setup(props, { emit }) {
onMounted(() => {
console.log(props.formTitle);
});
const submitTask = () => {
emit('task-added', { name: state.taskName });
};
return { submitTask };
}
Implementação Completa: Vue 2 vs Vue 3
Abaixo está a comparação completa de um componente de formulário de tarefas utilizando ambas as abordagens.
Componente Vue 2
<template>
<div class="task-form">
<h2>{{ formTitle }}</h2>
<input type="text" v-model="taskName" placeholder="Nome da Tarefa" />
<select v-model="taskPriority">
<option value="low">Baixa</option>
<option value="high">Alta</option>
</select>
<button @click="submitTask">Adicionar</button>
<p>Resumo: {{ upperCaseTask }} - {{ taskPriority }}</p>
</div>
</template>
<script>
export default {
props: {
formTitle: String
},
data() {
return {
taskName: '',
taskPriority: 'low'
};
},
mounted() {
console.log('Título do formulário: ' + this.formTitle);
},
computed: {
upperCaseTask() {
return this.taskName.toUpperCase();
}
},
methods: {
submitTask() {
this.$emit('task-added', {
name: this.taskName,
priority: this.taskPriority
});
}
}
};
</script>
Componente Vue 3
<template>
<div class="task-form">
<h2>{{ formTitle }}</h2>
<input type="text" v-model="state.taskName" placeholder="Nome da Tarefa" />
<select v-model="state.taskPriority">
<option value="low">Baixa</option>
<option value="high">Alta</option>
</select>
<button @click="submitTask">Adicionar</button>
<p>Resumo: {{ state.upperCaseTask }} - {{ state.taskPriority }}</p>
</div>
</template>
<script>
import { reactive, onMounted, computed } from 'vue';
export default {
props: {
formTitle: String
},
setup(props, { emit }) {
const state = reactive({
taskName: '',
taskPriority: 'low',
upperCaseTask: computed(() => state.taskName.toUpperCase())
});
onMounted(() => {
console.log('Título do formulário: ' + props.formTitle);
});
const submitTask = () => {
emit('task-added', {
name: state.taskName,
priority: state.taskPriority
});
};
return {
state,
submitTask
};
}
};
</script>