Vue3基础:响应式系统和组件入门

终于进入Vue3的世界了!这一期,我们学习Vue3最核心的概念:响应式系统和组件。
理解这两个概念,你就掌握了Vue3的80%。
响应式系统
什么是响应式?
// 普通变量 - 不是响应式的
let count = 0
count++
// Vue不知道count变了,UI不会更新
// 响应式变量 - Vue会追踪变化
const count = ref(0)
count.value++
// Vue知道count变了,自动更新UI
响应式 = 数据变化时,UI自动更新。
ref vs reactive
Vue3提供两种响应式API:
ref:包装单个值
import { ref } from 'vue'
const count = ref(0)
const message = ref('Hello')
const user = ref({ name: 'Vue' })
// 读取和修改需要.value
console.log(count.value) // 0
count.value++
reactive:包装对象/数组
import { reactive } from 'vue'
const state = reactive({
count: 0,
user: {
name: 'Vue'
}
})
// 直接访问属性
console.log(state.count) // 0
state.count++
// 数组
const list = reactive([1, 2, 3])
list.push(4)
怎么选?
| 场景 | 推荐 |
|---|---|
| 基本类型(数字、字符串) | ref |
| 对象 | 都可以 |
| 需要整体替换 | ref |
| 解构友好 | reactive |
我的建议:默认用ref,对象很大时用reactive。
模板中自动解包
import { ref } from 'vue'
const count = ref(0)
<!-- 不需要.value,自动解包 -->
<p>Count: {{ count }}</p>
<button>+1</button>
响应式丢失问题
// ❌ 解构会丢失响应性
const state = reactive({ count: 0 })
let { count } = state // count是普通数字
count++ // 不会触发更新!
// ✅ 使用toRefs
import { toRefs } from 'vue'
const { count } = toRefs(state) // count是ref
count.value++ // 会触发更新
// ✅ 使用toRef(单个属性)
import { toRef } from 'vue'
const count = toRef(state, 'count')
computed
计算属性:基于响应式数据派生新值,自动缓存。
import { ref, computed } from 'vue' const firstName = ref('Vue') const lastName = ref('3') // 只读计算属性 const fullName = computed(() => <code>${firstName.value} ${lastName.value}) // 可写计算属性 const fullName2 = computed({ get: () =>${firstName.value} ${lastName.value}, set: (val) => { [firstName.value, lastName.value] = val.split(' ') } }){{ fullName }}
watch
侦听数据变化:
import { ref, watch, watchEffect } from 'vue' const count = ref(0) // watch:明确指定侦听源 watch(count, (newVal, oldVal) => { console.log(<code>count changed: ${oldVal} -> ${newVal}) }) // watch多个源 watch([count, anotherRef], ([newCount, newAnother]) => { // ... }) // watchEffect:自动追踪依赖 watchEffect(() => { console.log(count is ${count.value}) }) // 立即执行 + 可停止 const stop = watchEffect(() => { // ... }, { immediate: true }) // 停止侦听 stop()
watch vs watchEffect:
watch:明确指定侦听源,可访问新旧值watchEffect:自动追踪,适合副作用
组件基础
定义组件
import { ref } from 'vue'
const count = ref(0)
<button>
Clicked {{ count }} times
</button>
button {
color: blue;
}
``是Vue3的语法糖,自动暴露变量给模板。
Props:父传子
<!-- Child.vue -->
// 定义props
const props = defineProps({
title: {
type: String,
required: true
},
count: {
type: Number,
default: 0
}
})
// 使用props
console.log(props.title)
<h1>{{ title }}</h1>
<p>Count: {{ count }}</p>
<!-- Parent.vue -->
import Child from './Child.vue'
Emits:子传父
<!-- Child.vue -->
const emit = defineEmits(['update', 'delete'])
function handleClick() {
emit('update', { id: 1, name: 'Vue' })
}
<button>Update</button>
<!-- Parent.vue -->
import Child from './Child.vue'
function onUpdate(data) {
console.log('Received:', data)
}
v-model:双向绑定
<!-- 自定义组件支持v-model -->
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
const value = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
})
Vue3.4+可以用defineModel:
const modelValue = defineModel()
插槽
<!-- Card.vue -->
<div class="card">
<div class="header">
</div>
<div class="content">
</div>
<div class="footer">
</div>
</div>
<!-- 使用 -->
<h1>Title</h1>
<p>Content goes here</p>
<button>OK</button>
作用域插槽:
<!-- List.vue -->
const items = ['Apple', 'Banana', 'Orange']
<ul>
<li>
</li>
</ul>
<!-- 使用 -->
{{ index + 1 }}. {{ item }}
模板语法
文本插值
<p>{{ message }}</p>
<p>{{ count + 1 }}</p>
<p>{{ formatDate(date) }}</p>
指令
<!-- 条件渲染 -->
<div>Type A</div>
<div>Type B</div>
<div>Other</div>
<!-- 列表渲染 -->
<ul>
<li>
{{ item.name }}
</li>
</ul>
<!-- 事件绑定 -->
<button>Click</button>
<button>+1</button>
...
<!-- 属性绑定 -->
<img />
<div>...</div>
<div>...</div>
<!-- 双向绑定 -->
...
<!-- 显示/隐藏 -->
<div>...</div>
v-if vs v-show
| 特性 | v-if | v-show |
|---|---|---|
| 渲染方式 | 条件为假时不渲染 | 始终渲染,用CSS隐藏 |
| 切换成本 | 高(销毁/重建) | 低(只改CSS) |
| 初始成本 | 低(不渲染) | 高(总是渲染) |
建议:频繁切换用v-show,条件很少改变用v-if。
Class和Style绑定
<!-- 对象语法 -->
<div></div>
<!-- 数组语法 -->
<div></div>
<!-- 混合 -->
<div></div>
<!-- Style对象 -->
<div></div>
<!-- Style数组 -->
<div></div>
生命周期
import { onMounted, onUpdated, onUnmounted } from 'vue'
onMounted(() => {
console.log('组件已挂载')
})
onUpdated(() => {
console.log('组件已更新')
})
onUnmounted(() => {
console.log('组件已卸载')
})
生命周期钩子:
| 选项式API | 组合式API |
|---|---|
| beforeCreate | setup() |
| created | setup() |
| beforeMount | onBeforeMount |
| mounted | onMounted |
| beforeUpdate | onBeforeUpdate |
| updated | onUpdated |
| beforeUnmount | onBeforeUnmount |
| unmounted | onUnmounted |
小结
你现在掌握了:
- 响应式系统(ref、reactive、computed、watch)
- 组件通信(props、emits、v-model、插槽)
- 模板语法(指令、条件、循环)
- 生命周期钩子
下一期,我们深入学习组合式API - Vue3最强大的特性。你会学会如何组织代码、复用逻辑、提取组合式函数。
练习任务:
- 创建一个计数器组件,用ref实现
- 创建一个父子组件,通过props和emits通信
- 实现一个自定义v-model组件
- 使用作用域插槽渲染列表
下期预告:《Vue3组合式API:代码组织的艺术》—— 组合式函数怎么写?如何复用逻辑?
Views: 1
