主题
指南
¥Guidelines
以下是 VueUse 函数的指南。你还可以将它们作为编写你自己的可组合函数或应用的参考。
¥Here are the guidelines for VueUse functions. You could also take them as a reference for authoring your own composable functions or apps.
你还可以通过 Anthony Fu 关于 VueUse 的演讲找到这些设计决策的一些原因以及编写可组合函数的一些技巧:
¥You can also find some reasons for those design decisions and also some tips for writing composable functions with Anthony Fu's talk about VueUse:
可组合的 Vue - 在 2021 年 VueDay 上
¥Composable Vue - at VueDay 2021
可组合的 Vue - VueConf China 2021(中文)
¥可组合的 Vue - at VueConf China 2021 (in Chinese)
通用
¥General
从
"vue-demi"
导入所有 Vue API¥Import all Vue APIs from
"vue-demi"
尽可能使用
ref
而不是reactive
¥Use
ref
instead ofreactive
whenever possible尽可能使用选项对象作为参数,以便将来的扩展更加灵活。
¥Use options object as arguments whenever possible to be more flexible for future extensions.
封装大量数据时,请使用
shallowRef
而不是ref
。¥Use
shallowRef
instead ofref
when wrapping large amounts of data.在使用像
window
这样的全局变量时使用configurableWindow
(等),以便在处理多窗口、测试模拟和 SSR 时更加灵活。¥Use
configurableWindow
(etc.) when using global variables likewindow
to be flexible when working with multi-windows, testing mocks, and SSR.当涉及浏览器尚未广泛实现的 Web API 时,也会输出
isSupported
标志¥When involved with Web APIs that are not yet implemented by the browser widely, also outputs
isSupported
flag在内部使用
watch
或watchEffect
时,还应尽可能使immediate
和flush
选项可配置¥When using
watch
orwatchEffect
internally, also make theimmediate
andflush
options configurable whenever possible使用
tryOnUnmounted
优雅地清除副作用¥Use
tryOnUnmounted
to clear the side-effects gracefully避免使用控制台日志
¥Avoid using console logs
当函数异步时,返回一个 PromiseLike
¥When the function is asynchronous, return a PromiseLike
另请阅读:最佳实践
¥Read also: Best Practice
ShallowRef
封装大量数据时,请使用 shallowRef
而不是 ref
。
¥Use shallowRef
instead of ref
when wrapping large amounts of data.
ts
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)
/* ... */
}
可配置的全局变量
¥Configurable Globals
当使用 window
或 document
等全局变量时,选项接口中支持 configurableWindow
或 configurableDocument
,使函数在多窗口、测试模拟、SSR 等场景下更加灵活。
¥When using global variables like window
or document
, support configurableWindow
or configurableDocument
in the options interface to make the function flexible when for scenarios like multi-windows, testing mocks, and SSR.
了解有关实现的更多信息:_configurable.ts
¥Learn more about the implementation: _configurable.ts
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)
}
/* ... */
}
使用示例:
¥Usage example:
ts
// in iframe and bind to the parent window
useActiveElement({ window: window.parent })
监视选项
¥Watch Options
在内部使用 watch
或 watchEffect
时,还应尽可能使 immediate
和 flush
选项可配置。例如 watchDebounced
¥When using watch
or watchEffect
internally, also make the immediate
and flush
options configurable whenever possible. For example watchDebounced
ts
import type { WatchOptions } from 'vue-demi'
// 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
我们使用 controls
选项,允许用户使用单次返回的函数来实现简单的用法,同时能够在需要时拥有更多的控制和灵活性。阅读更多:#362。
¥We use the controls
option allowing users to use functions with a single return for simple usages, while being able to have more controls and flexibility when needed. Read more: #362.
何时提供 controls
选项
¥When to provide a controls
option
该函数更常与单个
ref
或¥The function is more commonly used with single
ref
or¥Examples:
useTimestamp
useInterval
ts
// common usage
const timestamp = useTimestamp()
// more controls for flexibility
const { timestamp, pause, resume } = useTimestamp({ controls: true })
请参阅 useTimestamp
的源代码以实现适当的 TypeScript 支持。
¥Refer to useTimestamp
s source code for the implementation of proper TypeScript support.
何时不提供 controls
选项
¥When NOT to provide a controls
option
该函数更常用的是多次返回
¥The function is more commonly used with multiple returns
¥Examples:
useRafFn
useRefHistory
ts
const { pause, resume } = useRafFn(() => {})
isSupported
标志
¥isSupported
Flag
当涉及浏览器尚未广泛实现的 Web API 时,也会输出 isSupported
标志。
¥When involved with Web APIs that are not yet implemented by the browser widely, also outputs isSupported
flag.
例如 useShare
¥For example useShare
ts
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,
}
}
异步可组合项
¥Asynchronous Composables
当可组合项是异步的(例如 useFetch
时,最好从可组合项返回 PromiseLike 对象,以便用户能够等待该函数。这对于 Vue 的 <Suspense>
api 特别有用。
¥When a composable is asynchronous, like useFetch
it is a good idea to return a PromiseLike object from the composable so the user is able to await the function. This is especially useful in the case of Vue's <Suspense>
api.
使用
ref
来确定函数何时应解析,例如isFinished
¥Use a
ref
to determine when the function should resolve e.g.isFinished
将返回状态存储在变量中,因为它必须返回两次,一次在返回中,一次在 promise 中。
¥Store the return state in a variable as it must be returned twice, once in the return and once in the promise.
返回类型应该是返回类型和 PromiseLike 之间的交集,例如
UseFetchReturn & PromiseLike<UseFetchReturn>
¥The return type should be an intersection between the return type and a PromiseLike, e.g.
UseFetchReturn & PromiseLike<UseFetchReturn>
ts
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)
},
}
}
无渲染组件
¥Renderless Components
使用渲染函数代替 Vue SFC
¥Use render functions instead of Vue SFC
将属性封装在
reactive
中,轻松将它们作为属性传递到插槽中¥Wrap the props in
reactive
to easily pass them as props to the slot更喜欢使用函数选项作为 prop 类型,而不是自己重新创建它们
¥Prefer to use the functions options as prop types instead of recreating them yourself
仅当函数需要绑定目标时,才将插槽封装在 HTML 元素中
¥Only wrap the slot in an HTML element if the function needs a target to bind to
ts
import type { MouseOptions } from '@vueuse/core'
import { useMouse } from '@vueuse/core'
import { defineComponent, reactive } from 'vue-demi'
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)
}
},
})
有时一个函数可能有多个参数,在这种情况下,你可能需要创建一个新接口,将所有接口合并到组件 props 的单个接口中。
¥Sometimes a function may have multiple parameters, in that case, you maybe need to create a new interface to merge all the interfaces into a single interface for the component props.
ts
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>({ /* ... */ })