ガイドライン
ここにVueUse関数のガイドラインを示します。独自のコンポーザブル関数またはアプリを作成するための参照としても使用できます。
これらの設計決定の理由と、コンポーザブル関数の記述に関するヒントについては、Anthony FuによるVueUseに関する講演をご覧ください。
- Composable Vue - VueDay 2021にて
- 可组合的 Vue - VueConf China 2021にて (中国語)
一般
- すべてのVue APIを` "vue" `からインポートする
- 可能な限り`ref`を`reactive`の代わりに使用する
- 将来の拡張のために、可能な限り引数としてオプションオブジェクトを使用する。
- 大量のデータをラップする場合は、`ref`の代わりに`shallowRef`を使用する。
- `window`などのグローバル変数を使用する場合は、マルチウィンドウ、テストモック、SSRでの作業に柔軟に対応するために`configurableWindow`(など)を使用する。
- ブラウザでまだ広く実装されていないWeb APIに関連する場合は、`isSupported`フラグも出力する。
- 内部で`watch`または`watchEffect`を使用する場合は、可能な限り`immediate`および`flush`オプションも構成可能にする。
- 副作用を適切にクリアするために`tryOnUnmounted`を使用する。
- コンソールログの使用を避ける。
- 関数が非同期の場合は、PromiseLikeを返す。
こちらも参照: ベストプラクティス
ShallowRef
大量のデータをラップする場合は、`ref`の代わりに`shallowRef`を使用する。
export function useFetch<T>(url: MaybeRefOrGetter<string>) {
// use `shallowRef` to prevent deep reactivity
const data = shallowRef<T | undefined>()
const error = shallowRef<Error | undefined>()
fetch(toValue(url))
.then(r => r.json())
.then(r => data.value = r)
.catch(e => error.value = e)
/* ... */
}
構成可能なグローバル変数
`window`または`document`などのグローバル変数を使用する場合は、マルチウィンドウ、テストモック、SSRなどのシナリオで関数を柔軟にするために、オプションインターフェースで`configurableWindow`または`configurableDocument`をサポートする。
実装の詳細については、`_configurable.ts`をご覧ください。
import type { ConfigurableWindow } from '../_configurable'
import { defaultWindow } from '../_configurable'
export function useActiveElement<T extends HTMLElement>(
options: ConfigurableWindow = {},
) {
const {
// defaultWindow = isClient ? window : undefined
window = defaultWindow,
} = options
let el: T
// skip when in Node.js environment (SSR)
if (window) {
window.addEventListener('blur', () => {
el = window?.document.activeElement
}, true)
}
/* ... */
}
使用例
// in iframe and bind to the parent window
useActiveElement({ window: window.parent })
Watchオプション
内部で`watch`または`watchEffect`を使用する場合は、可能な限り`immediate`および`flush`オプションも構成可能にする。例:`watchDebounced`
import type { WatchOptions } from 'vue'
// extend the watch options
export interface WatchDebouncedOptions extends WatchOptions {
debounce?: number
}
export function watchDebounced(
source: any,
cb: any,
options: WatchDebouncedOptions = {},
): WatchStopHandle {
return watch(
source,
() => { /* ... */ },
options, // pass watch options
)
}
コントロール
簡単な用途では単一の戻り値で関数を使い、必要に応じてより多くの制御と柔軟性を持たせるために`controls`オプションを使用します。詳細はこちら:#362。
`controls`オプションを提供する場合
- 関数が単一の`ref`でより一般的に使用される場合、または
- 例:`useTimestamp` `useInterval`
// common usage
const timestamp = useTimestamp()
// more controls for flexibility
const { timestamp, pause, resume } = useTimestamp({ controls: true })
適切なTypeScriptサポートの実装については、`useTimestamp`のソースコードを参照してください。
`controls`オプションを提供しない場合
- 関数が複数の戻り値でより一般的に使用される場合
- 例:`useRafFn` `useRefHistory`
const { pause, resume } = useRafFn(() => {})
`isSupported`フラグ
ブラウザでまだ広く実装されていないWeb APIに関連する場合は、`isSupported`フラグも出力する。
export function useShare(
shareOptions: MaybeRef<ShareOptions> = {},
options: ConfigurableNavigator = {},
) {
const { navigator = defaultNavigator } = options
const isSupported = useSupported(() => navigator && 'canShare' in navigator)
const share = async (overrideOptions) => {
if (isSupported.value) {
/* ...implementation */
}
}
return {
isSupported,
share,
}
}
非同期コンポーザブル
`useFetch`のように、コンポーザブルが非同期の場合は、ユーザーが関数を`await`できるように、PromiseLikeオブジェクトをコンポーザブルから返すのが良いでしょう。これは、Vueの`
- 関数が解決されるべき時期を判断するために`ref`を使用する(例:`isFinished`)
- 戻り値の状態は、戻り値とPromiseの両方で返す必要があるため、変数に格納する。
- 戻り値の型は、戻り値の型とPromiseLikeの交差型にする(例:`UseFetchReturn & PromiseLike
`)
export function useFetch<T>(url: MaybeRefOrGetter<string>): UseFetchReturn<T> & PromiseLike<UseFetchReturn<T>> {
const data = shallowRef<T | undefined>()
const error = shallowRef<Error | undefined>()
const isFinished = ref(false)
fetch(toValue(url))
.then(r => r.json())
.then(r => data.value = r)
.catch(e => error.value = e)
.finally(() => isFinished.value = true)
// Store the return state in a variable
const state: UseFetchReturn<T> = {
data,
error,
isFinished,
}
return {
...state,
// Adding `then` to an object allows it to be awaited.
then(onFulfilled, onRejected) {
return new Promise<UseFetchReturn<T>>((resolve, reject) => {
until(isFinished)
.toBeTruthy()
.then(() => resolve(state))
.then(() => reject(state))
}).then(onFulfilled, onRejected)
},
}
}
レンダーレスコンポーネント
- Vue SFCの代わりにレンダー関数を使用する。
- プロパティを`reactive`でラップして、スロットにプロパティとして簡単に渡す。
- 自分で再作成する代わりに、関数のオプションをプロパティ型として使用する方が良い。
- 関数がバインドするターゲットを必要とする場合にのみ、スロットをHTML要素でラップする。
import type { MouseOptions } from '@vueuse/core'
import { useMouse } from '@vueuse/core'
import { defineComponent, reactive } from 'vue'
export const UseMouse = defineComponent<MouseOptions>({
name: 'UseMouse',
props: ['touch', 'resetOnTouchEnds', 'initialValue'] as unknown as undefined,
setup(props, { slots }) {
const data = reactive(useMouse(props))
return () => {
if (slots.default)
return slots.default(data)
}
},
})
関数が複数のパラメーターを持つ場合、コンポーネントのプロパティに対してすべてインターフェースを単一のインターフェースにマージするために、新しいインターフェースを作成する必要がある場合があります。
import type { TimeAgoOptions } from '@vueuse/core'
import { useTimeAgo } from '@vueuse/core'
interface UseTimeAgoComponentOptions extends Omit<TimeAgoOptions<true>, 'controls'> {
time: MaybeRef<Date | number | string>
}
export const UseTimeAgo = defineComponent<UseTimeAgoComponentOptions>({ /* ... */ })