Skip to main content

axios and alova, who handles what

· 3 min read
Alova Team

Short answer: they are layers, not rivals. axios is an HTTP client — it sends requests. alova is a request strategy layer — it decides how requests run: pagination, caching, retry, deduplication, token refresh. alova can use axios as its request adapter (@alova/adapter-axios), so choosing alova never means throwing axios away.

alova in one line: one set of request APIs that runs on the web, in apps (uni-app / Taro / mini-programs), and on the server (BFF).

Division of work

Concernaxiosalova
Sending HTTP requests✅ its jobDelegates to an adapter (fetch / XHR / axios / uni-app / Taro)
InterceptorsbeforeRequest / responded (your axios interceptors keep working)
Loading / error / data statesHand-written✅ auto-managed by useRequest and friends
Pagination, form, upload, SSE flowsHand-written✅ 20+ ready-made strategy hooks
Response cachingHand-written✅ multi-level cache (L1/L2) with declarative invalidation
Deduplicating identical concurrent requestsHand-written✅ built-in request sharing
Cross-platform (uni-app / Taro / mini-programs)Community wrappers✅ official adapters, same API everywhere
Server-side retry / rate limitingHand-writtenalova/server hooks

Where axios is genuinely stronger

An honest comparison cuts both ways:

  • Ecosystem and familiarity. axios has one of the largest communities in the JavaScript world. Nearly every edge case has a Stack Overflow answer; nearly every developer has used it. alova's community is far smaller.
  • Zero learning curve for your team. Everyone already knows axios.get(). alova introduces a Method abstraction and strategy hooks — a real (if small) mental shift.
  • If you only send a handful of requests, axios alone is simply enough. A strategy layer earns its keep only when you keep rewriting the same request logic.

What using both looks like

Keep your axios instance — including its interceptors and baseURL — and let alova drive it:

import axios from 'axios';
import { createAlova } from 'alova';
import { axiosRequestAdapter } from '@alova/adapter-axios';
import VueHook from 'alova/vue';

// your existing axios instance, untouched
const customAxios = axios.create({ baseURL: '/api', timeout: 10000 });

const alovaInst = createAlova({
statesHook: VueHook,
requestAdapter: axiosRequestAdapter({ axios: customAxios })
});

Before / after for a plain request in Vue 3:

// axios only: hand-written states
const loading = ref(false);
const data = ref({});
const error = ref(null);
const load = async () => {
try {
loading.value = true;
data.value = await customAxios.get('/todos');
} catch (e) {
error.value = e;
}
loading.value = false;
};
onMounted(load);

// axios + alova: states managed for you
const { loading, data, error } = useRequest(alovaInst.Get('/todos'));

Everything you configured for axios still applies: method config accepts all axios request options, and interceptor order is predictable — alova's beforeRequest fires before axios request interceptors, and alova's responded fires after axios response interceptors.

When you don't need alova

  • Your project fires only a few requests with no repeated pagination/caching/retry patterns — plain axios is the right call.
  • You are React-only, web-only, and already invested in React Query — switching buys you little unless you need cross-platform or server-side strategies.
  • You want a zero-abstraction stack — alova's Method + hooks model is one more concept, and that cost is real.

Next steps

uni-app pagination with one hook: race conditions, duplicate requests, loading state

· 5 min read
Alova Team

You've probably written pagination like this in a uni-app project — bump the page inside onReachBottom, fire a request, concatenate the array:

<script setup>
import { ref } from 'vue';
import { onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app';

const list = ref([]);
const page = ref(1);
const loading = ref(false);

const loadList = async () => {
loading.value = true;
const res = await new Promise(resolve => {
uni.request({
url: `https://api.example.com/goods?page=${page.value}&pageSize=10`,
success: resolve
});
});
list.value = [...list.value, ...res.data.list];
loading.value = false;
};

onReachBottom(() => {
page.value++;
loadList();
});

onPullDownRefresh(async () => {
page.value = 1;
list.value = [];
await loadList();
uni.stopPullDownRefresh();
});

loadList();
</script>

It runs, but it hides at least three problems you've probably hit in production:

  1. Race conditions: a user scrolls fast and triggers onReachBottom twice — pages 2 and 3 are both in flight. Whichever returns first gets concatenated first; on a weak network page 3 can easily arrive first, scrambling the list order.
  2. Duplicate requests: on some platforms onReachBottom fires several times in a row with no dedupe, so the same page is requested and appended twice.
  3. Missing edge cases: there's no "is this the last page" check, so it keeps firing empty requests at the bottom; pull-to-refresh and load-more share one loading flag, so the list flashes empty then slowly refills on refresh.

Fixing all three means adding: a dedupe flag, dropping stale responses by page number, an isLastPage calculation, and two separate states for refresh vs. append... Hand-written, that's usually dozens of lines of boilerplate unrelated to your list, and you repeat it on every list page.

What an ideal pagination solution needs

Forget any library for a moment. A "safe" pagination implementation needs at least:

  • Request-level dedupe: don't resend an in-flight request with the same params;
  • Responses land by page: a late response for an older page must not overwrite a newer one;
  • Append / refresh modes: load-more appends, pull-to-refresh resets;
  • Edge state: isLastPage, total, loading / preloading should be directly available;
  • Cross-platform: the same code must run in mini-programs, H5, and App.

The last point is the special constraint of uni-app — React Query and SWR target the browser's fetch by default and have no official adapter for the mini-program environment (uni.request). That's also why many uni-app projects end up back at hand-written pagination.

Using usePagination

alova is a request strategy library: one set of request APIs that runs on the web, in apps (uni-app / Taro / mini-programs), and on the server (BFF). Through the @alova/adapter-uniapp adapter it uses uni.request directly, and the usePagination strategy hook builds in the whole checklist above.

Install (note: the uni-app adapter currently supports Vue 3 uni-app only):

npm install alova @alova/adapter-uniapp @alova/shared --save

Create the alova instance; the adapter provides request adapting, storage adapting, and VueHook in one call:

// api/index.js
import { createAlova } from 'alova';
import AdapterUniapp from '@alova/adapter-uniapp';

export const alovaInst = createAlova({
baseURL: 'https://api.example.com',
...AdapterUniapp(),
responded(response) {
const { statusCode, data } = response;
if (statusCode >= 400) {
throw new Error('request error');
}
return data || null;
}
});

The full list page — the page management, race protection, edge checks, and dual loading you wrote by hand are now all returned by the hook:

<template>
<view v-for="item in data" :key="item.id" class="goods-item">
{{ item.name }}
</view>
<view v-if="loading">Loading...</view>
<view v-if="isLastPage">No more</view>
</template>

<script setup>
import { usePagination } from 'alova/client';
import { onReachBottom, onPullDownRefresh } from '@dcloudio/uni-app';
import { alovaInst } from '@/api';

const queryGoods = (page, pageSize) =>
alovaInst.Get('/goods', {
params: { page, pageSize }
});

const { loading, data, page, isLastPage, total, reload } = usePagination(
(page, pageSize) => queryGoods(page, pageSize),
{
append: true, // append mode: next page auto-concatenated to the bottom
initialPageSize: 10,
data: response => response.list,
total: response => response.total
}
);

// load more: just bump the page; dedupe, race handling, last-page check are inside the hook
onReachBottom(() => {
if (!isLastPage.value) {
page.value++;
}
});

// pull to refresh: reload resets to page one
onPullDownRefresh(async () => {
await reload();
uni.stopPullDownRefresh();
});
</script>

Compare: your business code is down to two actions — "page + 1" and "reload". The protection logic behind those three pitfalls (shared-request dedupe, response placement, isLastPage) needs no maintenance from you. usePagination also preloads the next page by default, so the next page is often already cached when you scroll.

The same code compiles and runs on WeChat mini-program, H5, and App — uni.request is always the one sending requests, and alova only handles the "how". If your project already uses axios (H5 side), it can keep working as alova's request adapter through @alova/adapter-axios; your interceptors stay untouched.

Live examples (pagination, load-more, and 24+ runnable demos): alova.js.org/examples

When you don't need it

Honestly, in these cases hand-writing is enough and adding any request library is overhead:

  • A single-page list, or fixed-size data: one uni.request plus one ref is the optimal solution.
  • H5-only project already deep into React Query / SWR: they're mature in pure browser environments; no reason to switch for a single-platform project.
  • uni-app Vue 2 project: @alova/adapter-uniapp supports Vue 3 only; evaluate before adopting on Vue 2.
  • A stable, battle-tested pagination wrapper already in place: working old code beats a new dependency; wait for a refactor window.

On the flip side, if your project has multiple paginated / load-more lists, has hit race-condition or duplicate-request issues in production, or maintains mini-program + H5 + App at once, usePagination is worth one try:

npm i alova