Skip to content

指南

¥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:

通用

¥General

  • "vue-demi" 导入所有 Vue API

    ¥Import all Vue APIs from "vue-demi"

  • 尽可能使用 ref 而不是 reactive

    ¥Use ref instead of reactive whenever possible

  • 尽可能使用选项对象作为参数,以便将来的扩展更加灵活。

    ¥Use options object as arguments whenever possible to be more flexible for future extensions.

  • 封装大量数据时,请使用 shallowRef 而不是 ref

    ¥Use shallowRef instead of ref when wrapping large amounts of data.

  • 在使用像 window 这样的全局变量时使用 configurableWindow(等),以便在处理多窗口、测试模拟和 SSR 时更加灵活。

    ¥Use configurableWindow (etc.) when using global variables like window 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

  • 在内部使用 watchwatchEffect 时,还应尽可能使 immediateflush 选项可配置

    ¥When using watch or watchEffect internally, also make the immediate and flush 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

当使用 windowdocument 等全局变量时,选项接口中支持 configurableWindowconfigurableDocument,使函数在多窗口、测试模拟、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

在内部使用 watchwatchEffect 时,还应尽可能使 immediateflush 选项可配置。例如 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

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

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 { defineComponent, reactive } from 'vue-demi'
import type { MouseOptions } from '@vueuse/core'
import { useMouse } from '@vueuse/core'

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>({ /* ... */ })

VueUse 中文网 - 粤ICP备13048890号