第4章:响应式系统与生命周期
第10节:响应式原理深入
4.10.1 响应式数据声明
Vue.js的响应式系统允许我们声明数据,当数据发生变化时,视图会自动更新。
data函数返回对象
在选项式API中,我们通过data函数返回一个对象来声明响应式数据:
export default {
data() {
return {
message: 'Hello Vue!',
count: 0,
user: {
name: '张三',
age: 25
},
items: [1, 2, 3]
}
}
}ref和reactive对比(组合式API预览)
在组合式API中,Vue 3提供了ref和reactive两个函数来声明响应式数据:
import { ref, reactive } from 'vue'
// 使用ref声明基本类型和对象类型
const count = ref(0) // 基本类型
const message = ref('Hello') // 字符串
const user = ref({ name: '张三' }) // 对象
// 使用reactive声明对象类型
const state = reactive({
count: 0,
message: 'Hello',
user: { name: '张三' },
items: [1, 2, 3]
})| 特性 | ref | reactive |
|---|---|---|
| 支持的类型 | 基本类型和对象类型 | 仅对象类型 |
| 访问方式 | 通过.value访问 |
直接访问 |
| 响应式代理 | 始终返回响应式代理 | 返回响应式代理 |
| 解构支持 | 不丢失响应性 | 直接解构会丢失响应性 |
4.10.2 响应式代理 vs 原始对象
Vue 3使用ES6的Proxy API实现响应式系统,而Vue 2使用的是Object.defineProperty。
Proxy API的优势
- 自动处理嵌套对象:Proxy可以自动为嵌套对象创建响应式代理
- 支持数组索引和长度变化:可以检测到数组索引赋值和长度修改
- 支持新增和删除属性:可以检测到对象属性的添加和删除
- 更好的性能:Proxy的性能比Object.defineProperty更好
响应式代理的使用
import { reactive } from 'vue'
const original = { count: 0 }
const proxy = reactive(original)
// 代理是响应式的
proxy.count++ // 触发更新
// 原始对象不是响应式的
original.count++ // 不会触发更新
// 代理和原始对象是不同的
console.log(proxy === original) // falsetoRaw函数
可以使用toRaw函数获取响应式代理对应的原始对象:
import { reactive, toRaw } from 'vue'
const proxy = reactive({ count: 0 })
const original = toRaw(proxy)
console.log(proxy === original) // false
original.count++ // 不会触发更新4.10.3 检测变化的注意事项
虽然Vue 3的响应式系统很强大,但仍有一些需要注意的地方:
对象属性添加/删除
在Vue 2中,直接添加或删除对象属性不会触发响应式更新,需要使用Vue.set或Vue.delete。在Vue 3中,使用reactive创建的对象可以直接添加或删除属性:
import { reactive } from 'vue'
const state = reactive({ count: 0 })
// Vue 3中可以直接添加属性
state.name = '张三' // 触发更新
// 可以直接删除属性
delete state.count // 触发更新数组索引修改
在Vue 2中,直接修改数组索引不会触发响应式更新,需要使用Vue.set或数组的变更方法。在Vue 3中,使用reactive创建的数组可以直接修改索引:
import { reactive } from 'vue'
const state = reactive({ items: [1, 2, 3] })
// Vue 3中可以直接修改数组索引
state.items[0] = 100 // 触发更新数组长度修改
在Vue 2中,直接修改数组长度不会触发响应式更新。在Vue 3中,使用reactive创建的数组可以直接修改长度:
import { reactive } from 'vue'
const state = reactive({ items: [1, 2, 3] })
// Vue 3中可以直接修改数组长度
state.items.length = 2 // 触发更新4.10.4 响应式API进阶
Vue.set / vm.$set
在Vue 2中,Vue.set或vm.$set用于向响应式对象添加响应式属性:
// Vue 2语法
this.$set(this.user, 'age', 25)
Vue.set(this.items, 0, 100)在Vue 3中,由于Proxy的特性,我们不再需要这些方法,可以直接添加或修改属性。
Vue.delete / vm.$delete
在Vue 2中,Vue.delete或vm.$delete用于删除响应式对象的属性:
// Vue 2语法
this.$delete(this.user, 'age')
Vue.delete(this.items, 0)在Vue 3中,我们可以直接使用delete操作符删除属性。
vm.$watch API使用
vm.$watch用于监听数据变化并执行回调函数:
export default {
data() {
return {
count: 0,
user: {
name: '张三',
age: 25
}
}
},
mounted() {
// 监听基本类型
this.$watch('count', (newVal, oldVal) => {
console.log(`count从${oldVal}变为${newVal}`)
})
// 监听对象属性
this.$watch('user.name', (newVal, oldVal) => {
console.log(`用户名从${oldVal}变为${newVal}`)
})
// 深度监听对象
this.$watch('user', (newVal, oldVal) => {
console.log('用户信息发生变化')
}, { deep: true })
// 监听多个属性
this.$watch(
() => [this.count, this.user.name],
([newCount, newName], [oldCount, oldName]) => {
console.log(`count: ${oldCount} -> ${newCount}`)
console.log(`name: ${oldName} -> ${newName}`)
}
)
}
}在组合式API中,可以使用watch函数:
import { ref, reactive, watch } from 'vue'
const count = ref(0)
const user = reactive({ name: '张三' })
// 监听ref
watch(count, (newVal, oldVal) => {
console.log(`count从${oldVal}变为${newVal}`)
})
// 监听reactive对象的属性
watch(() => user.name, (newVal, oldVal) => {
console.log(`用户名从${oldVal}变为${newVal}`)
})
// 深度监听reactive对象
watch(user, (newVal, oldVal) => {
console.log('用户信息发生变化')
}, { deep: true })总结
Vue.js的响应式系统是其核心特性之一,通过理解响应式数据声明、响应式代理和原始对象的区别,以及检测变化的注意事项,你可以更好地使用Vue的响应式系统。
Vue 3使用Proxy API实现响应式系统,相比Vue 2的Object.defineProperty有很多优势,包括自动处理嵌套对象、支持数组索引和长度变化、支持新增和删除属性等。
在下一节中,我们将学习Vue组件的生命周期钩子。