Skip to content

基础示例

基本用法

最基本的用法是传入一个返Promise 的异步函数

vue
<template>
  <div>
    <button @click="run">获取用户信息</button>
    <div v-if="loading">加载..</div>
    <div v-else-if="error">错误: {{ error.message }}</div>
    <div v-else>
      <p>用户 {{ data?.name }}</p>
      <p>邮箱: {{ data?.email }}</p>
    </div>
  </div>
</template>

<script setup>
import useRequest from '@pluve/use-request-vue';

const { data, loading, error, run } = useRequest(() => fetch('/api/user/1').then((res) => res.json()));
</script>

带参数的请求

vue
<template>
  <div>
    <input v-model="userId" placeholder="输入用户ID" />
    <button @click="() => run(userId)">获取用户信息</button>
    <div v-if="loading">加载..</div>
    <div v-else-if="error">错误: {{ error.message }}</div>
    <div v-else>{{ data }}</div>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import useRequest from '@pluve/use-request-vue';

const userId = ref('1');

const { data, loading, error, run } = useRequest((id) => fetch(`/api/user/${id}`).then((res) => res.json()), {
  manual: true, // 手动触发请求
});
</script>

使用插件的完整示

vue
<template>
  <div>
    <input
      v-model="keyword"
      @input="handleSearch"
      placeholder="搜索..."
    />

    <div v-if="loading">
      搜索..{{ retryCount ? `(${retryCount} 次重` : '' }}
    </div>

    <div v-else-if="error">
      错误: {{ error.message }}
      <button @click="run(keyword)">重试</button>
    </div>

    <div v-else>
      <div v-for="item in data?.list" :key="item.id" class="item">
        {{ item.name }}
      </div>
      <div v-if="data?.list.length === 0">
        没有找到相关结果
      </div>
    </div>

    <div v-if="!loading && data?.list?.length > 0" class="pagination">
      <button
        :disabled="pagination.page <= 1"
        @click="prevPage"
      >
        上一      </button>
      <span>{{ pagination.page }} /span>
      <button
        :disabled="!data?.hasMore"
        @click="nextPage"
      >
        下一      </button>
    </div>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue';
import  useRequest  from '@pluve/use-request-vue';
import {
  useDebouncePlugin,
  useRetryPlugin,
  useCachePlugin
} from '@pluve/use-request-vue';

const keyword = ref('');
const pagination = reactive({
  page: 1,
  pageSize: 10,
});

const {
  data,
  loading,
  error,
  run,
  retryCount,
  params: [currentKeyword = '']
} = useRequest(
  (kw, page = 1, pageSize = 10) => {
    const url = `/api/search?q=${encodeURIComponent(kw)}&page=${page}&pageSize=${pageSize}`;
    return fetch(url).then(res => {
      if (!res.ok) throw new Error('请求失败');
      return res.json();
    });
  },
  {
    manual: true,
    debounceInterval: 500,
    cacheKey: 'search-cache',
    retryCount: 3,
    onSuccess: (result) => {
      console.log('搜索成功:', result);
    },
    onError: (e) => {
      console.error('搜索失败:', e);
    },
  },
  [
    useDebouncePlugin(),
    useRetryPlugin(),
    useCachePlugin({
      cacheTime: 5 * 60 * 1000, // 5分钟
    }),
  ]
);

// 搜索处理
const handleSearch = () => {
  if (!keyword.value.trim()) return;
  pagination.page = 1; // 重置为第一  run(keyword.value, 1, pagination.pageSize);
};

// 上一const prevPage = () => {
  if (pagination.page <= 1) return;
  pagination.page--;
  run(keyword.value || currentKeyword, pagination.page, pagination.pageSize);
};

// 下一const nextPage = () => {
  if (!data.value?.hasMore) return;
  pagination.page++;
  run(keyword.value || currentKeyword, pagination.page, pagination.pageSize);
};

// 初始加载
// run('', 1, pagination.pageSize);
</script>

<style scoped>
input {
  padding: 8px 12px;
  margin-right: 8px;
  border: 1px solid #ddd;
  border-radius: 4px;
  width: 200px;
}

button {
  padding: 8px 16px;
  background-color: #4CAF50;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  margin: 0 4px;
}

button:disabled {
  background-color: #cccccc;
  cursor: not-allowed;
}

.item {
  padding: 12px;
  border-bottom: 1px solid #eee;
}

.item:hover {
  background-color: #f5f5f5;
}

.pagination {
  margin-top: 20px;
  display: flex;
  align-items: center;
  gap: 12px;
}
</style>

Vue Router 集成

vue
<template>
  <div>
    <h1>用户详情</h1>
    <div v-if="loading">加载..</div>
    <div v-else-if="error">
      加载失败: {{ error.message }}
      <button @click="retry">重试</button>
    </div>
    <div v-else>
      <h2>{{ user.name }}</h2>
      <p>邮箱: {{ user.email }}</p>
      <p>电话: {{ user.phone }}</p>
    </div>
  </div>
</template>

<script setup>
import { useRoute } from 'vue-router';
import  useRequest  from '@pluve/use-request-vue';
import { useRetryPlugin } from '@pluve/use-request-vue';

const route = useRoute();
const userId = computed(() => route.params.id);

const {
  data: user,
  loading,
  error,
  retry,
} = useRequest(
  (id) => fetch(`/api/users/${id}`).then(res => {
    if (!res.ok) throw new Error('用户不存);
    return res.json();
  }),
  {
    refreshDeps: [userId], // userId 变化时重新请    retryCount: 3,
  },
  [useRetryPlugin()]
);

// 也可以使watch 监听路由参数变化
// watch(() => route.params.id, (newId) => {
//   if (newId) {
//     run(newId);
//   }
// }, { immediate: true });
</script>

Pinia 集成

typescript
// stores/user.js
import { defineStore } from 'pinia';
import useRequest from '@pluve/use-request-vue';

export const useUserStore = defineStore('user', () => {
  const {
    data: user,
    loading,
    error,
    run: fetchUser,
  } = useRequest((id) => fetch(`/api/users/${id}`).then((res) => res.json()), {
    manual: true,
    initialData: null,
  });

  return {
    user,
    loading,
    error,
    fetchUser,
  };
});
vue
<!-- UserProfile.vue -->
<template>
  <div>
    <button @click="loadUser">加载用户</button>
    <div v-if="loading">加载..</div>
    <div v-else-if="error">错误: {{ error.message }}</div>
    <div v-else-if="user">
      <h2>{{ user.name }}</h2>
      <p>{{ user.email }}</p>
    </div>
  </div>
</template>

<script setup>
import { storeToRefs } from 'pinia';
import { useUserStore } from './stores/user';

const userStore = useUserStore();
const { user, loading, error } = storeToRefs(userStore);
const { fetchUser } = userStore;

const loadUser = () => {
  fetchUser(1);
};
</script>