Vue.observable 实现响应式状态管理
使用 Vue.observable 创建轻量级的响应式数据,适合简单的跨组件状态共享
问题
Vue.observable 是什么?如何使用它来实现轻量级的状态管理?
解答
什么是 Vue.observable
Vue.observable 可以让一个普通对象变成响应式数据。Vue 内部使用它来处理 data 函数返回的对象。
返回的对象可以直接用于渲染函数和计算属性,发生变更时会触发相应的更新。它可以作为最小化的跨组件状态存储器使用。
Vue.observable({ count: 1 })
其作用等同于:
new Vue({ data: { count: 1 } })
版本差异:
- Vue 2.x:传入的对象会直接被变更,返回的对象和传入的是同一个
- Vue 3.x:返回一个响应式代理,源对象不会变成响应式
使用场景
在非父子组件通信时,如果功能不复杂,使用 bus 或 vuex 会显得繁琐。这时 observable 是更好的选择。
创建状态管理文件 store.js:
import Vue from 'vue'
// 创建响应式状态
export const state = Vue.observable({
name: '张三',
age: 20
})
// 创建 mutations
export const mutations = {
changeName(name) {
state.name = name
},
setAge(age) {
state.age = age
}
}
在组件中使用:
<template>
<div>
姓名:{{ name }}
年龄:{{ age }}
<button @click="changeName('李四')">改变姓名</button>
<button @click="setAge(18)">改变年龄</button>
</div>
</template>
<script>
import { state, mutations } from '@/store'
export default {
computed: {
name() {
return state.name
},
age() {
return state.age
}
},
methods: {
changeName: mutations.changeName,
setAge: mutations.setAge
}
}
</script>
实现原理
源码位置:src/core/observer/index.js
核心流程是通过 observe 函数创建 Observer 实例,然后使用 defineReactive 将对象的每个属性转换为 getter/setter:
export function observe(value, asRootData) {
if (!isObject(value) || value instanceof VNode) {
return
}
// 创建 Observer 实例
return new Observer(value)
}
Observer 类会遍历对象的所有属性:
export class Observer {
constructor(value) {
this.value = value
this.dep = new Dep()
if (Array.isArray(value)) {
// 处理数组
} else {
this.walk(value)
}
}
walk(obj) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
defineReactive(obj, keys[i])
}
}
}
defineReactive 使用 Object.defineProperty 实现响应式:
export function defineReactive(obj, key, val, customSetter, shallow) {
const dep = new Dep()
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
const getter = property && property.get
const setter = property && property.set
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
const value = getter ? getter.call(obj) : val
// 依赖收集
if (Dep.target) {
dep.depend()
}
return value
},
set(newVal) {
const value = getter ? getter.call(obj) : val
if (newVal === value) return
if (setter) {
setter.call(obj, newVal)
} else {
val = newVal
}
// 触发更新
dep.notify()
}
})
}
关键点
- Vue.observable 将普通对象转换为响应式数据,适合轻量级状态管理
- 相比 Vuex,它更简单,适合不复杂的跨组件通信场景
- Vue 2.x 直接修改原对象,Vue 3.x 返回代理对象
- 底层通过 Observer 类和 defineReactive 方法实现,使用 Object.defineProperty 劫持属性
- 配合 computed 和 methods 可以实现类似 Vuex 的 state 和 mutations 模式
目录