Vue.js: Dados Reativos: ref/reactive

Última atualização: 2026-08-26

O sistema de reatividade do Vue 3 é o seu coração e alma — quando você modifica uma variável, todos os locais da página onde ela é usada são atualizados automaticamente. É por isso que o Vue é 10 vezes mais fácil de programar do que o jQuery. O sistema de reatividade é alimentado por duas APIs principais: ref() e reactive().

O ref é usado para envolver qualquer tipo de dado (números, strings, objetos), enquanto o reactive só pode envolver objetos. Os dois são usados de maneiras diferentes, e esta lição vai ajudar você a entender completamente a diferença.

1. O que você vai aprender


2. A história de uma página de contagem que deu errado

(1) Problema: Quando a contagem muda, a interface do usuário não é atualizada

Alice escreveu seu primeiro componente em Vue 3 para o painel de administração do site de comércio eletrônico. Ela tentou implementar um “contador de estoque de produtos”:

JS
// ❌ The "Broken" Version
let count = 0

function increment() {
  count++
  console.log(count)  // 1, 2, 3... It looks like
}
HTML
<template>
  <p>Stock: {{ count }}</p>
  <button @click="increment">+1</button>
</template>

Ela clica no botão 5 vezes. O console exibe 1, 2, 3, 4, 5. Mas a página ainda mostra “Estoque: 0”.

A gerente de produto, Charlie, olha por cima do ombro:

“Alice, por que a contagem não atualiza? Eu cliquei cinco vezes. O Vue está com problema?”

(2) Soluções reativas do Vue: ref() / reactive()

VUE
<!-- ✅ Correct Version -->
<template>
  <p>Stock: {{ count }}</p>
  <button @click="increment">+1</button>
</template>

<script setup>
import { ref } from 'vue'

// Use ref() Wrapper → Vue Knows it should be \"Reactive\"
const count = ref(0)

function increment() {
  // In JS Key Points .value
  count.value++
  // Automatic unpacking in templates,Not necessary .value
}
</script>

Agora, ao clicar no botão, a página é atualizada instantaneamente. A alteração na variável → o DOM é atualizado automaticamente.

(3) Receita

Depois de aprender sobre a reatividade do Vue 3:


3. Os princípios da reatividade: proxy + coleta de dependências

(1) Processo de resposta em três etapas

100%
sequenceDiagram
    participant T as Template
    participant C as Component
    participant R as Reactive<br/>(Proxy)
    participant D as Data
    
    T->>C: 1. Render (Read count)
    C->>R: 2. get count
    R->>D: 3. Read 0
    R->>R: 4. Track: Who's using it? count?
    R-->>C: 5. Return 0
    C-->>T: 6. Display "Stock: 0"
    
    Note over D,T: The user clicks the button
    
    D->>R: 7. set count = 1
    R->>R: 8. Trigger: Notice to All Users count the place
    R->>C: 9. Re-render
    C-->>T: 10. Display "Stock: 1"

(2) Pontos-chave

(3) Vue 2 x Vue 3: Uma comparação da reatividade

Dimensão Vista 2 Vista 3
API subjacente Object.defineProperty ES6 Proxy
Observadores de matriz Requer tratamento especial Suporte nativo
Mapa/Definir como favorito Não compatível Compatível
Adicionar novo imóvel Requer Vue.set() Resposta automática
Desempenho Média Melhor (Lazy Proxy)

4. ref(): Envolve qualquer valor

(1) Uso básico

JS
import { ref } from 'vue'

// Packaging Numbers
const count = ref(0)

// Packaging String
const name = ref('Alice')

// Boolean Packaging
const isVip = ref(false)

// Packaging null
const data = ref(null)

// Packaging Array
const items = ref([1, 2, 3])

// Items to Be Packaged
const user = ref({ name: 'Bob', age: 25 })

(2) Acesso aos valores: use .value no JavaScript; o modelo os descompacta automaticamente

VUE
<template>
  <!-- Using variable names directly in templates(Automatic Unpacking) -->
  <p>{{ count }}</p>          <!-- 0 -->
  <p>{{ name }}</p>          <!-- Alice -->
  <p>{{ user.name }}</p>     <!-- Bob -->
</template>

<script setup>
import { ref } from 'vue'
const count = ref(0)
const name = ref('Alice')
const user = ref({ name: 'Bob', age: 25 })

function logCount() {
  // In JS Must use .value
  console.log(count.value)  // 0
  console.log(name.value)   // Alice
  console.log(user.value.name)  // Bob
}
</script>

(3) A natureza “bidirecional” de ref

JS
import { ref } from 'vue'

const count = ref(0)

// Read
console.log(count.value)  // 0

// Edit
count.value = 1
console.log(count.value)  // 1

// Modify Nested Properties(ref When wrapping an object)
const user = ref({ name: 'Bob' })
user.value.name = 'Alice'  // ✅ Responsive
user.value = { name: 'Charlie' }  // ✅ The entire replacement also responds

5. reactive(): Objeto wrapper (Reatividade profunda)

(1) Uso básico

JS
import { reactive } from 'vue'

// Only objects can be passed
const state = reactive({
  count: 0,
  user: { name: 'Alice', age: 25 },
  items: [1, 2, 3]
})

// No access required .value
console.log(state.count)  // 0
console.log(state.user.name)  // Alice

// Edit Auto-Reply
state.count = 1
state.user.name = 'Bob'
state.items.push(4)

(2) Resposta detalhada

JS
const state = reactive({
  level1: {
    level2: {
      level3: {
        value: 'deep'
      }
    }
  }
})

// Changes made at any level are automatically reflected
state.level1.level2.level3.value = 'updated'  // ✅ Trigger an update

(3) Limitações da Programação Reativa

JS
// ❌ You cannot directly replace the entire object.(No longer responsive)
let state = reactive({ count: 0 })
state = reactive({ count: 1 })  // Wrong! state variable itself changed, but template uses old reference.

// ❌ Deconstruction results in the loss of responsiveness
const state = reactive({ count: 0, name: 'Alice' })
const { count, name } = state  // count/name Convert to a regular variable,Not responding

// ✅ Destructuring Reactivity: use toRefs
import { toRefs } from 'vue'
const { count, name } = toRefs(state)  // count/name.value Stay Responsive

6. As 5 principais diferenças entre ref e reactive

Aspecto ref reativo
Tipos encapsuláveis Qualquer valor (número, string, objeto, array) Apenas objetos (Object/Array)
Método de acesso .value (JS) Acesso direto (JS)
Acesso ao modelo Desempacotamento automático (sem .value) Acesso direto
Substituir tudo ref.value = {...} ✅ Responde reactive({...}) = ... ❌ Não responde
Desconstrução As respostas são preservadas (toRefs) As respostas são perdidas (toRefs obrigatório)

(4) Recomendações de seleção

Cenário Recomendação
Tipos primitivos (número/cadeia de caracteres/booleano) ref()
Objeto único responsivo É aceitável tanto ref({}) quanto reactive({})
Um estado composto por vários campos relacionados reactive({}) (como o Vuex)
Precisa ser substituído por completo ref({}) (mais flexível)
Passagem de objetos / emissão usando ref() (limites claros)

7. Reatividade superficial: shallowRef / shallowReactive

(1) Quando se deve usar respostas superficiais?

Quando você tem um objeto grande, mas está interessado apenas nas alterações nos campos de nível superior, uma resposta superficial pode melhorar o desempenho.

JS
import { shallowRef, shallowReactive, triggerRef } from 'vue'

// shallowRef:Follow Only .value Replace All,Do not track internal properties
const bigData = shallowRef({ items: [...50000 items] })

bigData.value.items.push('new')  // ❌ Do not trigger an update
bigData.value = { items: [newItems] }  // ✅ Trigger an update(Replace All)

// shallowReactive:Track only top-level properties,No in-depth response
const state = shallowReactive({
  user: { name: 'Alice' },
  settings: { theme: 'dark' }
})

state.user.name = 'Bob'  // ❌ Does not trigger
state.user = { name: 'Bob' }  // ✅ Trigger(Top-Level Property Replacement)

(2) Forçar uma atualização

JS
import { triggerRef } from 'vue'

const bigData = shallowRef({ items: [...] })
bigData.value.items.push('new')  // Do not respond by default
triggerRef(bigData)  // Manually Trigger an Update

Cenários típicos: tabelas grandes (100.000 linhas), listas extensas e situações em que as propriedades internas são modificadas com frequência, mas apenas o estado geral é relevante.


8. toRef / toRefs: Desconstrução com manutenção da reatividade

(1) toRefs: Desestruturação de um objeto reativo

JS
import { reactive, toRefs } from 'vue'

const state = reactive({
  count: 0,
  name: 'Alice'
})

// ❌ General Deconstruction:Missing Response
const { count, name } = state

// ✅ toRefs Deconstruction:Stay Responsive
const { count, name } = toRefs(state)

// Used in templates
// <p>{{ count }}</p>  <!-- Still responding -->

(2) toRef: Converte um valor comum em uma referência

JS
import { toRef, ref } from 'vue'

// Scene: Select one field from props
const props = defineProps({ count: Number })

// ❌ Directly Deconstructing Missing Responses
const { count } = props  // count It's ordinary number

// ✅ toRef Packaging
const count = toRef(props, 'count')  // count is ref(props.count)

// Usage
count.value++  // Responsive Changes props

9. 5 Armadilhas do Design Responsivo

(1) Armadilha 1: Substituir diretamente um objeto reativo

JS
let state = reactive({ count: 0 })
// ❌ state The entire variable has been changed,But the template uses the old version state
state = reactive({ count: 1 })
// ✅ Edit Properties
state.count = 1

(2) Armadilha 2: Desconstruir a reação e perder a resposta

JS
const state = reactive({ count: 0 })
// ❌ count It is a regular variable
const { count } = state
// ✅ Use toRefs
const { count } = toRefs(state)

(3) Armadilha 3: Arrays com shallowRef não reagem a mutações

JS
const items = shallowRef([1, 2, 3])
// ❌ shallowRef só rastreia substituição de .value, não mutações internas
items.value.push(4)  // NÃO dispara re-renderização
// ✅ Substitua o array inteiro
items.value = [...items.value, 4]
// ✅ Ou force o trigger
items.value.push(4)
triggerRef(items)

// Nota: Com ref() normal, push É reativo (ref envolve arrays em proxy reativo)
const list = ref([1, 2, 3])
list.value.push(4)  // ✅ Dispara re-renderização no Vue 3

(4) Armadilha 4: Modificar diretamente o conteúdo de um objeto encapsulado por um ref

JS
// It generally does not expire.,However, there are edge cases
const user = ref({ name: 'Alice' })
user.value.name = 'Bob'  // ✅ Response(ref Wrapped objects are automatically unwrapped)

(5) Armadilha 5: Confusão entre os tipos ref e reactive

JS
// ❌ Type error:reactive Cannot wrap the original value
const count = reactive(0)  // Error: value cannot be made reactive
// ✅ Use ref
const count = ref(0)

10. Exemplo completo: carrinho de compras responsivo

▶ Exemplo: 1. Uso básico de ref

VUE
<template>
  <div>
    <p>Count: {{ count }}</p>
    <button @click="increment">+1</button>
    <button @click="reset">Reset</button>
  </div>
</template>

<script setup>
import { ref } from 'vue'

const count = ref(0)

function increment() {
  count.value++  // JS Must be used in .value
}

function reset() {
  count.value = 0
}
</script>
▶ Experimente

▶ Exemplo: 2. Objetos wrapper reativos

VUE
<template>
  <div>
    <p>User: {{ state.user.name }}</p>
    <p>Age: {{ state.user.age }}</p>
    <button @click="birthday">+1 Year</button>
  </div>
</template>

<script setup>
import { reactive } from 'vue'

const state = reactive({
  user: { name: 'Alice', age: 25 }
})

function birthday() {
  state.user.age++  // Edit directly,Automatic Response
}
</script>
▶ Experimente

▶ Exemplo: 3. Comparação entre ref e reactive

JS
import { ref, reactive } from 'vue'

// ref Style
const userRef = ref({ name: 'Alice', age: 25 })
// Edit:
userRef.value.name = 'Bob'  // ✅ Response
userRef.value = { name: 'Charlie', age: 30 }  // ✅ Response

// reactive Style
const userReactive = reactive({ name: 'Alice', age: 25 })
// Edit:
userReactive.name = 'Bob'  // ✅ Response
// userReactive = reactive({...})  // ❌ Failure
▶ Experimente

▶ Exemplo: 4. toRefs — Desestruturação com preservação da resposta

VUE
<template>
  <div>
    <p>{{ count }} - {{ name }}</p>
    <button @click="increment">+1</button>
  </div>
</template>

<script setup>
import { reactive, toRefs } from 'vue'

const state = reactive({ count: 0, name: 'Alice' })
const { count, name } = toRefs(state)  // Stay Responsive

function increment() {
  count.value++  // count is ref, So use .value
}
</script>
▶ Experimente

▶ Exemplo: 5. Resposta superficial + triggerRef

JS
import { shallowRef, triggerRef } from 'vue'

// Large Table 100,000 rows → use shallowRef
const bigTable = shallowRef({
  rows: Array.from({ length: 100000 }, (_, i) => ({ id: i, name: `Row ${i}` }))
})

// Replace All(Highly efficient)
function reloadData(newRows) {
  bigTable.value = { rows: newRows }
}

// Modify Internal Properties(Not responding,Must be done manually trigger)
function updateRow(id, newName) {
  const row = bigTable.value.rows.find(r => r.id === id)
  if (row) {
    row.name = newName
    triggerRef(bigTable)  // Manually Trigger an Update
  }
}
▶ Experimente

▶ Exemplo: 6. Referência rápida para 5 erros comuns

Erro Sintoma Solução
let state = reactive({...}); state = {...} Substituir todo o estado; o modelo não é atualizado Modificar propriedade state.x = 1
const { count } = state Desconstrução Converter count em uma variável comum Usar toRefs(state)
reactive(0) Valor da embalagem original Erro Use ref() para o valor original
count.value++ no modelo erro modelo diretamente {{ count }}
array[index] = x com shallowRef Não dispara (shallow só rastreia .value) Use splice ou substitua o array inteiro

❓ Perguntas Frequentes

P: O que devo escolher: ref ou reactive? R: A recomendação oficial é usar exclusivamente ref (motivo: ref funciona com qualquer tipo e oferece uma substituição e passagem de props/funções mais claras no geral). reactive é adequado para cenários em que “o estado consiste em vários campos relacionados” (como o Vuex). Recomenda-se que os iniciantes aprendam primeiro ref e utilizem reactive quando estiverem mais proficientes.

P: Por que uma ref precisa de .value, mas um template não? R: .value é usado para indicar explicitamente ao mecanismo de JavaScript: “Este é um valor envolvido por uma ref; ele precisa ser tornado reativo”. O Vue analisa os templates por conta própria; ele sabe quais elementos são refs e os desembrulha automaticamente. Resumindo: .value é necessário no JavaScript (para evitar ambiguidade), enquanto os modelos desembrulham automaticamente as refs (por conveniência).

P: Ao usar um ref para envolver um objeto, preciso usar .value.x para acessar suas propriedades? R: Não. Após ref({name: 'Alice'}), o modelo usa {{ user.name }} diretamente, e o JavaScript usa user.value.name. O Vue lida automaticamente com o acesso às propriedades internas do objeto envolvido pelo ref.

P: A desestruturação reativa faz com que a reatividade seja perdida? Como isso pode ser resolvido? R: Envolva-a com toRefs(). Após const { count, name } = toRefs(state), count e name tornam-se referências; tanto {{ count }} no modelo quanto count.value no JavaScript permanecem reativos.

P: Qual é a diferença entre shallowRef e ref? R: ref é uma referência profunda (alterações nas propriedades internas também são acionadas), enquanto shallowRef rastreia apenas a substituição de .value como um todo (não rastreia alterações internas). Quando o conjunto de dados é grande (mais de 100.000 registros) e você está interessado apenas na substituição geral, usar shallowRef proporciona melhor desempenho.

P: Quais são as causas mais comuns para a falha do comportamento reativo? R: As 5 mais comuns são: (1) substituir diretamente um objeto reativo; (2) desestruturar um objeto reativo sem usar toRefs; (3) envolver um valor primitivo em um ref que não reage (use reactive em vez disso ou mantenha o ref); (4) criar um ref fora de um setup (fazendo com que ele perca a reatividade); (5) usar shallowRef/shallowReactive e esperar que mutações profundas disparem atualizações.

P: O reactive pode envolver o Map e o Set? R: Sim. O reactive do Vue 3, que é implementado usando Proxy, suporta Map, Set, WeakMap e WeakSet. O Vue 2 não suporta isso.


📖 Resumo


📝 Exercícios

  1. Questões básicas (Dificuldade: ⭐)

    Escreva um contador simples de quantidade para um carrinho de compras usando ref:

    • Valor inicial 1
    • Botão “+”: Adicionar 1
    • O botão “-” diminui o valor em 1
    • Desativar o botão “-” quando a quantidade for ≤ 0 (:desativado dinamicamente)
    • Desativar o botão “+” quando a quantidade for ≥ 99
  2. Problemas avançados (Dificuldade: ⭐⭐)

    Crie um formulário para editar o perfil do usuário usando o Reactive:

    • O campo “state” contém três campos: name, email e age
    • Vinculação bidirecional para três campos de entrada (usando o evento @input; abordaremos o v-model na próxima aula)
    • O botão “Redefinir” restaura os valores padrão
    • O botão “Enviar” exibe o estado completo no console
  3. Problema desafiador (Dificuldade: ⭐⭐⭐)

    Implementação de um aplicativo completo de tarefas (camada de dados):

    1. Envolva a matriz todos com reactive
    2. Cada tarefa inclui { id, text, done }
    3. Métodos disponíveis: addTodo(text) / toggleTodo(id) / removeTodo(id) / clearDone()
    4. Forneça as seguintes propriedades calculadas: activeCount (número de itens não concluídos) e doneCount (número de itens concluídos)
    5. Exibir a lista completa de tarefas + contagem no modelo

    Dados iniciais: { id: 1, text: 'Learn Vue 3', done: false }

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%