state篇
createGlobalState:全局响应式状态
- 通过scope.run来管理依赖,作用域销毁时自动清除依赖,避免内存泄漏
export function createGlobalState<T>(
stateFactory: () => T,
): CreateGlobalStateReturn<T> {
let initialized = false
let state: T
const scope = effectScope(true)
return () => {
if (!initialized) {
state = scope.run(stateFactory)!
initialized = true
}
return state
}
}
createInjectionState:通过provide/inject实现状态管理
- 随时可以更新provide
- 内部闭包通过symbol避免重复
export function createInjectionState<Arguments extends Array<any>, Return>(
composable: (...args: Arguments) => Return,
): readonly [useProvidingState: (...args: Arguments) => Return, useInjectedState: () => Return | undefined] {
const key: string | InjectionKey<Return> = Symbol('InjectionState')
const useProvidingState = (...args: Arguments) => {
const state = composable(...args)
provide(key, state)
return state
}
const useInjectedState = () => inject(key)
return [useProvidingState, useInjectedState]
}
createSharedComposable:共享状态
export function createSharedComposable<Fn extends((...args: any[]) => any)>(composable: Fn): Fn {
let subscribers = 0
let state: ReturnType<Fn> | undefined
let scope: EffectScope | undefined
const dispose = () => {
subscribers -= 1
if (scope && subscribers <= 0) {
scope.stop()
state = undefined
scope = undefined
}
}
return <Fn>((...args) => {
subscribers += 1
if (!state) {
scope = effectScope(true)
state = scope.run(() => composable(...args))
}
tryOnScopeDispose(dispose)
return state
})
}
export function tryOnScopeDispose(fn: Fn) {
if (getCurrentScope()) {
onScopeDispose(fn)
return true
}
return false
}
useManualRefHistory:具有改变记录、撤销和恢复状态
export function useManualRefHistory<Raw, Serialized = Raw>(
source: Ref<Raw>,
options: UseManualRefHistoryOptions<Raw, Serialized> = {},
): UseManualRefHistoryReturn<Raw, Serialized> {
const {
clone = false,
dump = defaultDump<Raw, Serialized>(clone),
parse = defaultParse<Raw, Serialized>(clone),
setSource = fnSetSource,
} = options
function _createHistoryRecord(): UseRefHistoryRecord<Serialized> {
return markRaw({
snapshot: dump(source.value),
timestamp: timestamp(),
})
}
const last: Ref<UseRefHistoryRecord<Serialized>> = ref(_createHistoryRecord()) as Ref<UseRefHistoryRecord<Serialized>>
const undoStack: Ref<UseRefHistoryRecord<Serialized>[]> = ref([])
const redoStack: Ref<UseRefHistoryRecord<Serialized>[]> = ref([])
const _setSource = (record: UseRefHistoryRecord<Serialized>) => {
setSource(source, parse(record.snapshot))
last.value = record
}
const commit = () => {
undoStack.value.unshift(last.value)
last.value = _createHistoryRecord()
if (options.capacity && undoStack.value.length > options.capacity)
undoStack.value.splice(options.capacity, Infinity)
if (redoStack.value.length)
redoStack.value.splice(0, redoStack.value.length)
}
const clear = () => {
undoStack.value.splice(0, undoStack.value.length)
redoStack.value.splice(0, redoStack.value.length)
}
const undo = () => {
const state = undoStack.value.shift()
if (state) {
redoStack.value.unshift(last.value)
_setSource(state)
}
}
const redo = () => {
const state = redoStack.value.shift()
if (state) {
undoStack.value.unshift(last.value)
_setSource(state)
}
}
const reset = () => {
_setSource(last.value)
}
const history = computed(() => [last.value, ...undoStack.value])
const canUndo = computed(() => undoStack.value.length > 0)
const canRedo = computed(() => redoStack.value.length > 0)
return {
source,
undoStack,
redoStack,
last,
history,
canUndo,
canRedo,
clear,
commit,
reset,
undo,
redo,
}
}
请求篇
useAsyncState:类似react-query
- 传入一个Promise或返回Promise的异步方法,实现类似react-query的调用
export function useAsyncState<Data, Shallow extends boolean = true>(
promise: Promise<Data> | ((...args: any[]) => Promise<Data>),
initialState: Data,
options?: UseAsyncStateOptions<Shallow, Data>,
): UseAsyncStateReturn<Data, Shallow> {
const {
immediate = true,
delay = 0,
onError = noop,
onSuccess = noop,
resetOnExecute = true,
shallow = true,
throwError,
} = options ?? {}
const state = shallow ? shallowRef(initialState) : ref(initialState)
const isReady = ref(false)
const isLoading = ref(false)
const error = ref<unknown | undefined>(undefined)
async function execute(delay = 0, ...args: any[]) {
if (resetOnExecute)
state.value = initialState
error.value = undefined
isReady.value = false
isLoading.value = true
if (delay > 0)
await promiseTimeout(delay)
const _promise = typeof promise === 'function'
? promise(...args)
: promise
try {
const data = await _promise
state.value = data
isReady.value = true
onSuccess(data)
}
catch (e) {
error.value = e
onError(e)
if (throwError)
throw error
}
finally {
isLoading.value = false
}
return state.value as Data
}
if (immediate)
execute(delay)
return {
state: state as Shallow extends true ? Ref<Data> : Ref<UnwrapRef<Data>>,
isReady,
isLoading,
error,
execute,
}
}
事件篇
useEventListener:事件注册、手动|自动卸载事件
export function useEventListener(...args: any[]) {
let target: MaybeComputedRef<EventTarget> | undefined
let events: Arrayable<string>
let listeners: Arrayable<Function>
let options: any
if (isString(args[0]) || Array.isArray(args[0])) {
[events, listeners, options] = args
target = defaultWindow
}
else {
[target, events, listeners, options] = args
}
if (!target)
return noop
if (!Array.isArray(events))
events = [events]
if (!Array.isArray(listeners))
listeners = [listeners]
const cleanups: Function[] = []
const cleanup = () => {
cleanups.forEach(fn => fn())
cleanups.length = 0
}
const register = (el: any, event: string, listener: any) => {
el.addEventListener(event, listener, options)
return () => el.removeEventListener(event, listener, options)
}
const stopWatch = watch(
() => unrefElement(target as unknown as MaybeElementRef),
(el) => {
cleanup()
if (!el)
return
cleanups.push(
...(events as string[]).flatMap((event) => {
return (listeners as Function[]).map(listener => register(el, event, listener))
}),
)
},
{ immediate: true, flush: 'post' },
)
const stop = () => {
stopWatch()
cleanup()
}
tryOnScopeDispose(stop)
return stop
}