/vfetcher

Vue composables for fetching data, based on unjs/ofetch

Primary LanguageTypeScriptMIT LicenseMIT

vfetcher

English | 简体中文

Vue composables for fetching data, based on unjs/ofetch.

$ pnpm i vfetcher

Features

  • Carefully designed API: Intentionally mimicking the nuxt/useFetch API to maintain consistency as much as possible and reduce migration burden.
  • More features: Throttling/debouncing/polling/pagination out of the box... and more features to come!

Usage

Visit examples to see the config examples, visit test to see the usage examples.

Re-export ofetch

vfetcher re-export all exports of ofetch so you can directly use ofetch:

import { $fetch } from 'vfetcher/ofetch'

Basic Examples

The first parameter of useAsyncData is a callback function, and the second parameter is its configuration object. By default, the callback is automatically called during initialization:

import { useAsyncData } from 'vfetcher'

const { data } = useAsyncData(() => $fetch('/return-ok'))
watchEffect(() => {
  console.log(data.value) // Ref
  // -> null
  // -> 'ok'
})

Use the immediate: false option to prevent the request from being sent automatically during initialization:

const { data, execute, refresh } = useAsyncData(() => $fetch('return-ok'), {
  immediate: false
})

watchEffect(() => {
  console.log(data.value)
})

// -> null

// await refresh() // as alias of execute
await execute()

// -> 'ok'

The watch option accepts the same values as the first parameter of Vue's watch. When these reactive variables change, a request will be automatically sent:

const dep = ref('foo')
useAsyncData(() => $fetch('ok'), {
  watch: [dep]
})
// request to => 'ok'
dep.value = 'bar'
// request to => 'ok'

The ready option accepts a reactive boolean value or a function that returns a boolean. A request is only sent when the result is true; if the result is false, the execute is terminated immediately, and no request is sent. This is useful when using dependent requests with specific conditions:

const ready = ref(false)
const { execute } = useAsyncData(() => $fetch('ok'), {
  immediate: false,
  ready
})
await execute()
// the promise will be resolved immediately and no request will be sent
ready.value = true
await execute()
// request to => 'ok'

The status return value of useAsyncData indicates the current state. By monitoring the status, you can implement callbacks in different situations. The status is initially idle, indicating an idle state; it changes to pending before the request is sent, indicating a waiting response. Upon successful request, it changes to success, indicating success, or to error if the request fails:

const { status } = useAsyncData(() => $fetch('ok'))

// Equivalent to `onSuccess` hook:
watch(status, (v) => {
  if (v !== 'success')
    return

  onSuccess()
})

Use the pollingInterval option to perform polling requests:

useAsyncData(() => $fetch('ok'), {
  pollingInterval: 2000 // 2 seconds
})

// request to => 'ok'
// wait 2 seconds...
// request to => 'ok'

const { execute } = useAsyncData(() => $fetch('ok'), {
  pollingInterval: 2000, // 2 seconds
  immediate: false
})

// ...
// Will not poll until `execute` is called for the first time.

await execute() // request to => 'ok'
// wait 2 seconds...
// request to => 'ok'

Use the debounceInterval option to apply debouncing:

const { execute } = useAsyncData(() => $fetch('ok'), {
  debounceInterval: 2000 // 2 seconds
})

await execute()
// request to => 'ok'
execute()
execute()

// after about 2 seconds
// request to => 'ok'

Use the throttleInterval option to apply throttling:

const { execute } = useAsyncData(() => $fetch('ok'), {
  throttleInterval: 2000 // 2 seconds
})

await execute()
// request to => 'ok'
execute()
execute()

// after about 2 seconds
await execute()
// request to => 'ok'

Customize the default options

You could customize useAsyncData to configure your favorite default options:

import { useAsyncData as $ } from 'vfetcher'

export const useAsyncData = $.create({
  immediate: false
})

const { execute } = useAsyncData(() => $fetch('ok'))
// ...
await execute()

The new useAsyncData will extend the default options of the previous one:

import { useAsyncData as $1 } from 'vfetcher'

const $2 = $1.create({
  debounceInternal: 150
})
const useAsyncData = $2.create({
  immediate: false
})

useAsyncData(() => $fetch('ok'))
// Equal to:
// `useAsyncData(() => $fetch('ok'), { debounceInternal: 150, immediate: false })`

Returns

Except for execute/refresh, all other variables are wrapped by ref:

  • data: The result returned by the asynchronous request, defaulting to null, with the result being the return value of ofetch.
  • pending: A boolean value indicating whether the data is still being fetched.
  • error: The error object if the data fetch fails, otherwise null.
  • status: A string representing the state of the data request (idle, pending, success, error).
  • execute/refresh: A function used to manually trigger the request.

Options

  • immediate: A boolean value indicating whether to make a request during initialization. Defaults to true.
  • watch: Watches a set of reactive sources, similar to the first parameter type of the Vue watch method. When the reactive sources change, a new request will be made. By default, it watches the request URL and request parameters (detailed below), but you can manually set it to false to disable this feature.
  • pollingInterval: Can be a reactive value. Pass a number, in milliseconds, to indicate the interval time for polling. By default, polling is not enabled.
  • debounceInterval: Can be a reactive value. Pass a number, in milliseconds, to indicate the debounce delay time. By default, debounce is not enabled.
  • throttleInterval: Can be a reactive value. Pass a number, in milliseconds, to indicate the throttle wait time. By default, throttling is not enabled.

useFetch

useFetch is almost a combination of useAsyncData and ofetch:

  • The first parameter is basically the same as the first parameter of ofetch.
  • The second parameter accepts all configuration options of both useAsyncData and ofetch.
  • The return value is the same as that of useAsyncData.

Thanks to ofetch, the data will be automatically converted to the appropriate type:

const { data: d1 } = useFetch('/return-ok')
watchEffect(() => {
  console.log(d1.value)
  // -> null
  // -> 'ok'
})

const { data: d2 } = useFetch('/return-json')
watchEffect(() => {
  console.log(d2.value)
  // -> null
  // -> { one: 1 }
})

Request parameters such as the request path, headers, query, body, etc., can be passed in as reactive values, which will automatically trigger requests when they change:

const url = ref('return-ok')
const query = ref({ one: '1' })
const { data } = useFetch(url, { query })
watchEffect(() => {
  console.log(data.value)
})

// -> null
// -> 'ok'

url.value = 'return-query'

// -> { one: '1' }

query.value = { two: '2' }

// -> { two: '2' }

Similar to useAsyncData, you can configure your preferred default parameters, as mentioned above:

import { useFetch as $ } from 'vfetcher'

export const useFetch = $.create({
  // ...
})

useFetch shares the same return values and options as useAsyncData. It also accepts all options from ofetch, and by default, it monitors the following parameters from ofetch:

  • URL: The request path URL.
  • method: The request method type.
  • query: Query parameters.
  • params: Just an alias for query.
  • body: The request body.
  • headers: Request headers.
  • baseURL: The base URL for the request.

... For other general options of ofetch, please refer to the ofetch official documentation.

Pagination

Use usePagination function to handle pagination.

import { usePagination } from 'vfetcher'

Basic usage

usePagination is a wrapper around useFetch:

  • The first parameter is the same as in usePagination.
  • The second parameter is a configuration object, containing all the configurations of useFetch.
  • The return value includes all the return values of useFetch.

You can directly use the configuration options of useFetch.

usePagination('ok', {
  immediate: false
})

It automatically merge the params of pagination to query:

usePagination('getByPage')
// request to => `/getByPage?current=1&pageSize=10`

Compared to useFetch, some new return values have been added, which are also responsive:

const { pageCurrent } = usePagination('getByPage')
// request to => `/getByPage?current=1&pageSize=10`
pageCurrent.value = 2
// request to => `/getByPage?current=2&pageSize=10`

Get pageSize and total data counts by lodash - get , or you can configure the param key manually:

const { data, total } = usePagination('getByPage', {
  totalKey: 'res.total'
})
watchEffect(() => {
  console.log(data.value)

  if (data.value)
    console.log(total.value)
})

// -> null
// -> { res: { total:10, data:[ /* ... */]} }
// -> 10

Same as useFetch, you can also configure the default params you like. See above for details:

import { usePagination as $ } from 'vfetcher'

export const usePagination = $.create({
  // ...
})

New Return Values and Options

usePagination includes all the options and return values of useFetch. In addition, it also has some options and return values specific to itself.

All new return values are reactive variables:

  • pageCurrent: Indicates the current page number (number).
  • pageSize: Indicates the number of items per page (number).
  • total: Indicates the total number of items (read-only number).
  • pageTotal: Indicates the total number of pages (read-only number).

New Options

  • pageCurrentKey: Indicates the key name for the current page number, used in the query. Default is 'current'.
  • pageSizeKey: Indicates the key name for the number of items per page, used in the query. Default is 'pageSize'.
  • defaultPageSize: Indicates the default number of items per page (number), useful when immediate: true. Default is 10.
  • totalKey: The key name for fetching the total number of items, obtained from the returned data using lodash - get. Default is 'total'.
  • pageTotalKey: The key name for fetching the total number of pages, obtained from the returned data using lodash - get. Default is 'totalPage'.