88 lines
2.3 KiB
TypeScript
88 lines
2.3 KiB
TypeScript
import { reactive, onBeforeUnmount, type Reactive } from 'vue'
|
||
import type { IObservable } from '@/core/state/IObservable.ts'
|
||
|
||
/**
|
||
* Vue Hook: useObservable
|
||
* 支持深层解构赋值,直接修改触发 ObservableImpl 通知 + Vue 响应式更新
|
||
* @example
|
||
* interface AppState {
|
||
* count: number
|
||
* user: { name: string; age: number }
|
||
* items: number[]
|
||
* }
|
||
*
|
||
* // 创建 ObservableImpl
|
||
* const obs = new ObservableImpl<AppState>({
|
||
* count: 0,
|
||
* user: { name: 'Alice', age: 20 },
|
||
* items: []
|
||
* })
|
||
*
|
||
* export default defineComponent({
|
||
* setup() {
|
||
* // 深层解构 Hook
|
||
* const { count, user, items } = useObservable(obs)
|
||
*
|
||
* const increment = () => {
|
||
* count += 1 // 触发 ObservableImpl 通知 + Vue 更新
|
||
* }
|
||
*
|
||
* const changeAge = () => {
|
||
* user.age = 30 // 深层对象也能触发通知
|
||
* }
|
||
*
|
||
* const addItem = () => {
|
||
* items.push(42) // 数组方法拦截,触发通知
|
||
* }
|
||
*
|
||
* return { count, user, items, increment, changeAge, addItem }
|
||
* }
|
||
* })
|
||
*/
|
||
export function useObservable<T extends object>(observable: IObservable<T>): Reactive<T> {
|
||
// 创建 Vue 响应式对象
|
||
const state = reactive({} as T)
|
||
|
||
/**
|
||
* 将 ObservableImpl Proxy 映射到 Vue 响应式对象
|
||
* 递归支持深层对象
|
||
*/
|
||
function mapKeys(obj: any, proxy: any) {
|
||
(Object.keys(proxy) as (keyof typeof proxy)[]).forEach(key => {
|
||
const value = proxy[key]
|
||
if (typeof value === 'object' && value !== null) {
|
||
// 递归创建子对象 Proxy
|
||
obj[key] = reactive({} as typeof value)
|
||
mapKeys(obj[key], value)
|
||
} else {
|
||
// 基本类型通过 getter/setter 同步
|
||
Object.defineProperty(obj, key, {
|
||
enumerable: true,
|
||
configurable: true,
|
||
get() {
|
||
return proxy[key]
|
||
},
|
||
set(val) {
|
||
proxy[key] = val
|
||
},
|
||
})
|
||
}
|
||
})
|
||
}
|
||
|
||
// 获取 ObservableImpl 的 Proxy
|
||
const refsProxy = observable.toRefsProxy()
|
||
mapKeys(state, refsProxy)
|
||
|
||
// 订阅 ObservableImpl,保持响应式同步
|
||
const unsubscribe = observable.subscribe(() => {
|
||
// 空实现即可,getter/setter 已同步
|
||
})
|
||
|
||
onBeforeUnmount(() => {
|
||
unsubscribe()
|
||
})
|
||
|
||
return state
|
||
}
|