import { reactive, isRef, isReactive, toRaw, ComputedRef, toRef, effectScope, unref } from 'vue' import { v4 } from 'uuid' import { Store, StoreState, StoreToRefs } from './types' /** * 判断是否 ComputedRef 类型 * @param obj * @returns */ function isComputed(obj: unknown): obj is ComputedRef { return !!(isRef(obj) && Reflect.has(obj, 'effect')) } /** * 定义一个 store 对象 * @param createStore * @param id * @returns */ export function defineStore(createStore: () => T, id = v4()) { const scope = effectScope() const store = scope.run(() => createStore()) as T // 将 store 中的响应式属性转化为 ref 属性 const $toRefs = () => { const result = Object.create(null) for (const key in store) { const value = store[key] if (isRef(value)) { result[key] = value } else if (isReactive(value)) { result[key] = toRef(store, key) } } return result as StoreToRefs } // 更新 state 状态 const $setState = (callback: (state: StoreState) => void) => { const state = Object.create(null) for (const key in store) { const value = store[key] if (!isComputed(value) && (isRef(value) || isReactive(value))) { state[key] = value } } callback(reactive(state)) } // 将 state 重设为初始状态 const $reset = () => { const newScope = effectScope() const newStore = scope.run(() => createStore()) as T for (const key in store) { const item = store[key] if (!isComputed(item) && isRef(item)) { item.value = unref(newStore[key]) } if (isReactive(item)) { for (const prop in item) { item[prop] = newStore[key][prop] } } } newScope.stop() } // 销毁当前的 store const $dispose = () => { scope.stop() } const $store: Store = reactive({ $id: id, $toRefs, $setState, $reset, $dispose, ...store }) return () => $store } /** * 将 store 中的响应式属性转化为 ref 属性 * @param store * @returns */ export function storeToRefs(store: Store) { const raw = toRaw(store) const result = Object.create(null) for (const key in raw) { const value = raw[key] if (isRef(value)) { result[key] = value } else if (isReactive(value)) { result[key] = toRef(raw, key) } } return result as StoreToRefs }