Reactive Data
Reactivity is the most fundamental concept in Vue3.
In this chapter, you will understand what reactivity is and learn to userefandreactiveto manage data, and implement category filtering functionality.
What is Reactivity?
Reactivity means:When data changes, the page updates automatically.。
The traditional JS approach is like this: after modifying a variable, you have to manually update the DOM.
Vue3's reactive system automatically tracks data changes and notifies the view to refresh—you only need to care about data, not the DOM.
Example
import { ref } from 'vue'
// Variables wrapped by ref are reactive
const count = ref(0)
// After modifying count, {{ count }} in the template updates automatically
function increment() {
count.value++ // Note: in JS you need .value to access
}
</script>
<template>
<p>Click count: {{ count }}</p>
<!-- Auto-unwrapped in the template, no .value needed -->
<button @click="increment">+1</button>
</template>
After clicking the button, the number displayed on the page automatically changes—this is reactivity.
You don't need to write anydocument.querySelectororinnerHTMLto update the page.
ref — Handling Primitive Types
refis the most commonly used reactive API in Vue3, suitable for wrapping primitive type data.
Primitive types include: strings, numbers, booleans, null, undefined.
Syntax
Example
import { ref } from 'vue'
// Creating a ref
const name = ref('EXAMPLE') // String
const age = ref(18) // Number
const isVip = ref(false) // Boolean
// Reading .value
console.log(name.value) // "EXAMPLE"
console.log(age.value) // 18
// Modifying .value
name.value = 'Hello EXAMPLE'
age.value++
isVip.value = !isVip.value
</script>
<template>
<p>Name: {{ name }}</p>
<!-- In templates, ref is auto-unwrapped, no .value needed -->
<p>Age: {{ age }}</p>
<p>VIP:{{ isVip }}</p>
</template>
The most important thing to remember:In JS code you need .value, but not in templates.This is the most common mistake beginners make.
reactive — Handling Objects and Arrays
reactiveSuitable for wrapping reference type data such as objects or arrays.
Unlike ref, reactive does not need.value.value, and you can directly access properties.
Example
import { reactive } from 'vue'
// Wrap an object with reactive
const user = reactive({
name: 'Xiaoming',
age: 20,
hobbies: ['programming', 'running']
})
// Directly access and modify, no .value needed
console.log(user.name) // "Xiao Ming"
user.name = 'Xiao Hua' // Reactive update
user.hobbies.push('reading') // Array operations are also reactive
</script>
<template>
<p>Name: {{ user.name }}</p>
<p>Age: {{ user.age }}</p>
<ul>
<li v-for="(hobby, index) in user.hobbies" :key="index">
{{ hobby }}
</li>
</ul>
</template>
Two Limitations of reactive
| Limitation | Description | Solution |
|---|---|---|
| Can only wrap object types | Cannot pass in primitive types like strings, numbers | Use ref for primitive types |
| Cannot be replaced as a whole | Cannot assign the entire object to a new object | Use Object.assign() or switch to ref |
Example
// Error: cannot replace the reactive object as a whole
const state = reactive({ count: 0 })
// state = { count: 1 } // This won't work, reactivity will be lost
// Correct approach 1: modify properties instead of replacing the whole
state.count = 1
// Correct approach 2: use ref to wrap the object (ref allows replacing the whole)
const stateRef = ref({ count: 0 })
stateRef.value = { count: 1 } // It works! Because ref's .value can be replaced
ref vs reactive: When to Use Which
| Scenario | Recommendation | Example |
|---|---|---|
| Single string/number/boolean | ref | const name = ref('小明') |
| Single array | ref | const list = ref([]) |
| Single object | Both ref and reactive work | const user = ref({}) |
| Multiple unrelated states | Use ref for each | One ref per line |
| A group of strongly related states | reactive | e.g., form data, configuration collection |
The most common practice in actual development is:Always use ref, including objects and arrays。
This keeps the code style consistent, and you don't need to switch your mental model between ref and reactive.
The following chapters of this tutorial uniformly use ref to manage all state. When encountering places that require
.valuereactive, those places will be specially noted.
computed — Computed Properties
computedis "data computed from existing data."
It has two core features:Caching——when dependencies don't change, it won't recalculate;Automatic tracking——when dependencies change, it updates automatically.
Example
import { ref, computed } from 'vue'
const articles = ref([
Getting Started with Vue3
{ id: 2, title: 'Promise Detailed Explanation', category: 'JavaScript' },
Grid Layout
{ id: 4, title: 'Composition API', category: 'Vue' }
])
const activeCategory = ref('All')
// computed filters articles based on activeCategory
const filteredArticles = computed(() => {
if (activeCategory.value === 'All') {
return articles.value
}
return articles.value.filter(a => a.category === activeCategory.value)
})
// computed can also be used for statistics
const totalCount = computed(() => articles.value.length)
const filteredCount = computed(() => filteredArticles.value.length)
</script>
<template>
<p>Total {{ totalCount }} articles, currently showing {{ filteredCount }}</p>
<!-- Category buttons -->
<button @click=activeCategory = 'All'>All</button>
<button @click="activeCategory = 'Vue'">Vue</button>
<button @click="activeCategory = 'JavaScript'">JavaScript</button>
<button @click="activeCategory = 'CSS'">CSS</button>
<!-- Filtered article list -->
<div v-for="article in filteredArticles" :key="article.id">
<h3>{{ article.title }}</h3>
<span>{{ article.category }}</span>
</div>
</template>
WheneveractiveCategorychanges,filteredArticlesit will automatically recalculate, and the page refreshes automatically.
Note: computed returns a ref, so accessing it in JS also requires
.value.value. However, in the above example, we used it directly in the template, where it is auto-unwrapped.
Hands-on: Add Category Filtering to the Blog
Apply computed to the blog homepage to filter articles by clicking category buttons.
Example
import { ref, computed } from 'vue'
const articles = ref([
{ id: 1, title: 'Vue3 入门完全Guide', summary: 'fromzerostart学 Vue3', category: 'Vue', date: '2024-05-10' },
{ id: 2, title: 'JS Asynchronous Programming In Detail', summary: Understand Promise and async/await, category: 'JavaScript', date: '2024-05-08' },
{ id: 3, title: Practical CSS Grid Layout, summary: 'Implement Responsive Layout with Grid', category: 'CSS', date: '2024-05-05' },
{ id: 4, title: 'Vue3 Reactivity Principles', summary: 'Deep understanding of ref and reactive', category: 'Vue', date: '2024-05-03' },
{ id: 5, title: 'Flexbox Complete Guide', summary: 'Learn flex layout in one article', category: 'CSS', date: '2024-05-01' },
])
// All categories (deduplicated)
const categories = computed(() => {
const cats = articles.value.map(a => a.category)
return ['All', ...new Set(cats)] // Prepend 'All'
})
// Currently selected category
const activeCategory = ref('All')
// Filter articles by category
const filteredArticles = computed(() => {
if (activeCategory.value === 'All') {
return articles.value
}
return articles.value.filter(a => a.category === activeCategory.value)
})
</script>
<template>
<div class="home">
<h2>Latest Articles</h2>
<!-- Category filter buttons -->
<div class="category-bar">
<button
v-for="cat in categories"
:key="cat"
:class="{ active: activeCategory === cat }"
@click="activeCategory = cat"
>
{{ cat }}
</button>
</div>
<!-- Filtered result count -->
<p class="result-info">Total {{ filteredArticles.length }} articles</p>
<!-- Article list -->
<div class="article-grid">
<div v-for="article in filteredArticles" :key="article.id" class="card">
<span class="tag">{{ article.category }}</span>
<h3>{{ article.title }}</h3>
<p>{{ article.summary }}</p>
<span class="date">{{ article.date }}</span>
</div>
</div>
</div>
</template>
<style scoped>
.category-bar {
display: flex;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.category-bar button {
padding: 6px 16px;
border: 1px solid #ddd;
border-radius: 20px;
background: #fff;
cursor: pointer;
font-size: 14px;
transition: all 0.2s;
}
.category-bar button.active {
background: #427DB8;
color: #fff;
border-color: #427DB8;
}
.category-bar button:hover {
border-color: #427DB8;
}
.result-info {
color: #999;
font-size: 14px;
margin-bottom: 16px;
}
</style>
Now when you click different category buttons, the article list will filter in real time. You don't need to write any DOM manipulation code; you only need to focus on the logic of data changes.
Chapter Summary
In this chapter, you mastered the three core APIs of the Vue3 reactivity system: ref wraps primitive types (using .value in JS), reactive wraps objects, and computed derives computed data.
Through computed + category filter buttons, you experienced the development pattern of 'data-driven views' — change the data, the page updates automatically.
Other Extensions