perf(hooks): perf useSignal

This commit is contained in:
Soybean 2024-04-25 15:00:47 +08:00
parent d460e5cc6d
commit 5d45cef1f0

View File

@ -1,5 +1,12 @@
import { computed, shallowRef, triggerRef } from 'vue'; import { computed, ref, shallowRef, triggerRef } from 'vue';
import type { ComputedGetter, DebuggerOptions, ShallowRef, WritableComputedOptions, WritableComputedRef } from 'vue'; import type {
ComputedGetter,
DebuggerOptions,
Ref,
ShallowRef,
WritableComputedOptions,
WritableComputedRef
} from 'vue';
type Updater<T> = (value: T) => T; type Updater<T> = (value: T) => T;
type Mutator<T> = (value: T) => void; type Mutator<T> = (value: T) => void;
@ -7,26 +14,27 @@ type Mutator<T> = (value: T) => void;
/** /**
* Signal is a reactive value that can be set, updated or mutated * Signal is a reactive value that can be set, updated or mutated
* *
* ```ts * @example
* const count = useSignal(0); * ```ts
* const count = useSignal(0);
* *
* // `watchEffect` * // `watchEffect`
* watchEffect(() => { * watchEffect(() => {
* console.log(count()); * console.log(count());
* }); * });
* *
* // watch * // watch
* watch(count, value => { * watch(count, value => {
* console.log(value); * console.log(value);
* }); * });
* *
* // useComputed * // useComputed
* const double = useComputed(() => count() * 2); * const double = useComputed(() => count() * 2);
* const writeableDouble = useComputed({ * const writeableDouble = useComputed({
* get: () => count() * 2, * get: () => count() * 2,
* set: value => count.set(value / 2) * set: value => count.set(value / 2)
* }); * });
* ``` * ```
*/ */
export interface Signal<T> { export interface Signal<T> {
(): Readonly<T>; (): Readonly<T>;
@ -56,14 +64,43 @@ export interface Signal<T> {
* @param mutator * @param mutator
*/ */
mutate(mutator: Mutator<T>): void; mutate(mutator: Mutator<T>): void;
/**
* Get the reference of the signal
*
* Sometimes it can be useful to make `v-model` work with the signal
*
* ```vue
* <template>
* <input v-model="model.count" />
* </template>;
*
* <script setup lang="ts">
* const state = useSignal({ count: 0 }, { useRef: true });
*
* const model = state.getRef();
* </script>
* ```
*/
getRef(): Readonly<ShallowRef<Readonly<T>>>;
} }
export interface ReadonlySignal<T> { export interface ReadonlySignal<T> {
(): Readonly<T>; (): Readonly<T>;
} }
export function useSignal<T>(initialValue: T): Signal<T> { export interface SignalOptions {
const state = shallowRef(initialValue); /**
* Whether to use `ref` to store the value
*
* @default false use `sharedRef` to store the value
*/
useRef?: boolean;
}
export function useSignal<T>(initialValue: T, options?: SignalOptions): Signal<T> {
const { useRef } = options || {};
const state = useRef ? (ref(initialValue) as Ref<T>) : shallowRef(initialValue);
return createSignal(state); return createSignal(state);
} }
@ -101,5 +138,7 @@ function createSignal<T>(state: ShallowRef<T> | WritableComputedRef<T>): Signal<
triggerRef(state); triggerRef(state);
}; };
signal.getRef = () => state as Readonly<ShallowRef<Readonly<T>>>;
return signal; return signal;
} }