订阅state变化
订阅state变化
Vue.js的核心特性之一是响应式系统,它允许开发者声明式地描述UI与数据之间的关系。当应用状态发生变化时,视图会自动更新。理解如何订阅这些变化对于构建复杂应用至关重要。
响应式基础
Vue使用Object.defineProperty
(Vue 2)或Proxy
(Vue 3)来实现响应式。当组件实例被创建时,Vue会递归地将data对象的所有属性转换为getter/setter:
// Vue 2响应式原理简化示例
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
console.log(`获取${key}: ${val}`)
return val
},
set(newVal) {
if (newVal !== val) {
console.log(`设置${key}: ${newVal}`)
val = newVal
}
}
})
}
watch选项
组件选项中的watch
允许观察特定数据源并在变化时执行回调:
export default {
data() {
return {
count: 0,
user: {
name: 'Alice',
age: 25
}
}
},
watch: {
// 简单路径
count(newVal, oldVal) {
console.log(`count从${oldVal}变为${newVal}`)
},
// 深度观察
user: {
handler(newVal) {
console.log('用户信息变化:', newVal)
},
deep: true,
immediate: true
},
// 观察嵌套属性
'user.name'(newName) {
console.log('用户名变为:', newName)
}
}
}
$watch方法
除了选项式API,还可以在组件实例上使用$watch
方法:
created() {
this.$watch(
() => this.count,
(newVal, oldVal) => {
console.log('$watch监听到count变化:', oldVal, '->', newVal)
},
{ immediate: true }
)
// 观察计算属性
this.$watch(
() => this.count * 2,
(newVal) => {
console.log('count的两倍现在是:', newVal)
}
)
}
计算属性
计算属性是基于它们的响应式依赖进行缓存的:
computed: {
formattedUser() {
return `${this.user.name} (${this.user.age}岁)`
},
// 带setter的计算属性
fullName: {
get() {
return `${this.firstName} ${this.lastName}`
},
set(newValue) {
const names = newValue.split(' ')
this.firstName = names[0]
this.lastName = names[names.length - 1]
}
}
}
副作用与watchEffect
Vue 3引入了watchEffect
,自动追踪其依赖:
import { watchEffect, ref } from 'vue'
setup() {
const count = ref(0)
watchEffect(() => {
console.log('count值:', count.value)
// 自动追踪count.value作为依赖
})
// 停止观察
const stop = watchEffect(() => {...})
stop()
return { count }
}
自定义响应式逻辑
使用reactive
和ref
创建响应式对象:
import { reactive, ref, watch } from 'vue'
export default {
setup() {
const state = reactive({
items: [],
loading: false
})
const searchQuery = ref('')
watch(
searchQuery,
async (newQuery) => {
state.loading = true
state.items = await fetchItems(newQuery)
state.loading = false
},
{ debounce: 300 }
)
return { state, searchQuery }
}
}
响应式工具函数
Vue提供了一些响应式工具函数:
import { isRef, unref, toRef, toRefs } from 'vue'
setup(props) {
// 解构props保持响应性
const { title } = toRefs(props)
// 创建对props.count的ref
const countRef = toRef(props, 'count')
function logValue(maybeRef) {
// 如果是ref则返回.value,否则返回原值
console.log(unref(maybeRef))
}
return { title, countRef, logValue }
}
性能优化
过度订阅state变化可能导致性能问题:
watch(
() => state.items,
() => {
// 避免在items数组内容变化时频繁触发
},
{ deep: true, flush: 'post' }
)
// 使用markRaw跳过响应式转换
import { markRaw } from 'vue'
const heavyObject = markRaw({ ... })
与第三方状态管理集成
订阅Vuex或Pinia的状态变化:
// Vuex示例
computed: {
...mapState(['count'])
},
watch: {
count(newVal) {
console.log('Vuex count变化:', newVal)
}
}
// Pinia示例
import { useStore } from '@/stores/counter'
setup() {
const store = useStore()
store.$subscribe((mutation, state) => {
console.log('存储变化:', mutation.type, state)
})
return {}
}
调试响应式依赖
Vue 3提供了调试钩子:
import { onRenderTracked, onRenderTriggered } from 'vue'
setup() {
onRenderTracked((event) => {
console.log('依赖被追踪:', event)
})
onRenderTriggered((event) => {
console.log('依赖触发更新:', event)
})
}
响应式模式实践
构建自动保存功能:
// 自动保存表单
const form = reactive({
title: '',
content: '',
lastSaved: null
})
let saveTimer
watch(
() => ({ ...form }),
(newForm) => {
clearTimeout(saveTimer)
saveTimer = setTimeout(async () => {
await saveToServer(newForm)
form.lastSaved = new Date()
}, 1000)
},
{ deep: true }
)
响应式边界
理解响应式系统的限制:
// 数组变更检测注意事项
const list = reactive(['a', 'b'])
// 这些能触发响应
list.push('c')
list.splice(0, 1)
// 这些不能
list[0] = 'x' // Vue 2
list.length = 0 // Vue 2
// 动态添加属性
const obj = reactive({})
// Vue 2需要Vue.set
Vue.set(obj, 'newProp', 123)
// Vue 3可以直接赋值
obj.newProp = 123
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:与Vue DevTools集成
下一篇:热模块替换支持