如何在 Vue.js 中实现 API 数据获取、过滤、排序和分页

www.jswusn.com Other 2025-03-25 09:36:50 14次浏览

在本文中,我将分享如何在 Vue.js 应用中从 API 获取数据,并实现过滤、排序和分页等实用功能。这些功能在处理诸如产品列表或内容库等项目时尤为有用,用户需要一种便捷的方式来浏览数据。我将逐步引导你完成从数据获取到添加交互控件的整个过程,以便你可以在自己的项目中应用这些技术。让我们开始吧!

数据获取

<template>
  <main class="min-h-screen bg-neutral-200 py-4">
    <section class="w-[1280px] mx-auto">
      <ul class="mt-1">
        <li v-for="product in products" :key="product.id">
          {{ product.id }}. {{ product.title }} - ${{ product.price }} - {{ product.category }}
        </li>
      </ul>
      <p v-if="!loading && products.length === 0">暂无数据</p>
      <p v-if="loading">加载中...</p>
      <p v-if="error">{{ error }}</p>
    </section>
</main>
</template>

<script setup lang="ts">
import { onMounted, ref } from'vue';

interface Product {
id: number;
title: string;
price: number;
description: string;
category: string;
image: string;
rating: {
    rate: number;
    count: number;
  };
}

constBASE_URL = 'https://www.yourdomain.com/products';

const products = ref<Product[]>([]);
const loading = ref<boolean>(false);
const error = ref<string | null>(null);

constfetchProducts = async () => {
  loading.value = true;
try {
    const response = awaitfetch(BASE_URL);
    if (!response.ok) {
      thrownewError(`HTTP 错误! 状态码: ${response.status}`);
    }
    products.value = await response.json();
  } catch (err) {
    error.value = err instanceofError ? err.message : '发生未知错误';
  } finally {
    loading.value = false;
  }
};

onMounted(() =>fetchProducts());
</script>

onMounted:
在 Vue.js 中,onMounted 是生命周期钩子之一,它在组件挂载到 DOM 后执行。简单来说,onMounted 中的代码会在组件完全准备好并显示在屏幕上时运行。

在上面的示例中,onMounted 用于在组件加载时调用 fetchProducts 函数。该函数从外部 API 获取产品数据并将其存储在 products 变量中。这确保了用户在查看页面时数据是可用的。它特别适用于初始化数据,例如获取产品列表、文章或页面所需的任何动态信息。

v-for:
v-for 是 Vue.js 中的一个指令,用于根据数组或对象中的数据动态渲染元素。在上面的示例中,v-for 用于根据从 API 获取的数据自动生成产品列表。

实现过滤、排序和分页

<template>
  <main class="min-h-screen bg-neutral-200 py-4">
    <section class="w-[1280px] mx-auto">
      <div class="flex gap-x-4 items-center">
        <input v-model="searchTerm" type="search" placeholder="搜索..." />
        <select v-model="category">
          <option value="" selected>分类:</option>
          <option value="men's clothing">男装</option>
          <option value="women's clothing">女装</option>
          <option value="jewelery">珠宝</option>
          <option value="electronics">电子产品</option>
        </select>
        <select v-model="sortBy">
          <option value="" selected>排序方式:</option>
          <option value="title">标题</option>
          <option value="price">价格</option>
        </select>
        <select v-model="orderBy">
          <option value="" selected>排序顺序:</option>
          <option value="asc">升序</option>
          <option value="desc">降序</option>
        </select>
        <select v-model="limit">
          <option value="" selected>每页显示:</option>
          <option v-for="index in 20" :value="index">{{ index }}</option>
        </select>
      </div>
      <div class="flex gap-x-4 items-center mt-4">
        <button
          :disabled="page === 1"
          @click="page > 1 && page--"
          class="disabled:cursor-not-allowed"
        >
          上一页
        </button>
        <span>第 {{ page }} 页,共 {{ totalPage }} 页</span>
        <button
          :disabled="page === totalPage"
          @click="page < totalPage && page++"
          class="disabled:cursor-not-allowed"
        >
          下一页
        </button>
      </div>
      <ul class="mt-1">
        <li v-for="product in limitedProducts" :key="product.id">
          {{ product.id }}. {{ product.title }} - ${{ product.price }} - {{ product.category }}
        </li>
      </ul>
      <p v-if="!loading && searchedProducts.length === 0">暂无数据</p>
      <p v-if="loading">加载中...</p>
      <p v-if="error">{{ error }}</p>
    </section>
</main>
</template>

<script setup lang="ts">
import { computed, onMounted, ref } from'vue';

interface Product {
id: number;
title: string;
price: number;
description: string;
category: string;
image: string;
rating: {
    rate: number;
    count: number;
  };
}

constBASE_URL = 'https://www.yourdomain.com/products';

const products = ref<Product[]>([]);
const loading = ref<boolean>(false);
const error = ref<string | null>(null);
const orderBy = ref<string>('');
const category = ref<string>('');
const sortBy = ref<string>('');
const searchTerm = ref<string>('');
const limit = ref<string>('');
const page = ref<number>(1);

const totalPage = computed(() => {
const itemsPerPage = limit.value ? Number(limit.value) : 10;
returnMath.ceil(searchedProducts.value.length / itemsPerPage);
});

constfetchProducts = async () => {
  loading.value = true;
try {
    const response = awaitfetch(BASE_URL);
    if (!response.ok) {
      thrownewError(`HTTP 错误! 状态码: ${response.status}`);
    }
    products.value = await response.json();
  } catch (err) {
    error.value = err instanceofError ? err.message : '发生未知错误';
  } finally {
    loading.value = false;
  }
};

const orderByProducts = computed(() => {
const sortedProducts = [...products.value];

if (sortBy.value === 'title') {
    return orderBy.value === 'desc'
      ? sortedProducts.sort((a, b) => b.title.localeCompare(a.title))
      : sortedProducts.sort((a, b) => a.title.localeCompare(b.title));
  } elseif (sortBy.value === 'price') {
    return orderBy.value === 'desc'
      ? sortedProducts.sort((a, b) => b.price - a.price)
      : sortedProducts.sort((a, b) => a.price - b.price);
  } elseif (orderBy.value === 'desc') {
    return sortedProducts.sort((a, b) => b.id - a.id);
  }

return sortedProducts;
});

const filteredProducts = computed(() => {
if (category.value) {
    return orderByProducts.value.filter(
      (product) => product.category === category.value
    );
  }

return orderByProducts.value;
});

const searchedProducts = computed(() => {
if (searchTerm.value) {
    return filteredProducts.value.filter((product) =>
      product.title
        .toLocaleLowerCase()
        .includes(searchTerm.value.toLocaleLowerCase())
    );
  }

return filteredProducts.value;
});

const limitedProducts = computed(() => {
const itemsPerPage = limit.value ? Number(limit.value) : 10;
return searchedProducts.value.slice(
    (page.value - 1) * itemsPerPage,
    page.value * itemsPerPage
  );
});

onMounted(() =>fetchProducts());
</script>

v-model:
Vue 中的 v-model 指令实现了表单输入与其关联的响应式变量之间的双向数据绑定。这意味着输入字段的任何更改都会立即更新链接的变量,而变量的更改也会反映在输入字段中。例如,在此实现中,v-model  searchTermcategorysortByorderBy  limit输入一起使用,确保用户的选择或输入动态更新应用程序状态。

computed:
computed 属性用于根据应用程序的状态执行响应式计算。它们仅在依赖项更改时重新计算,从而实现高效更新。在此实现中,orderByProductsfilteredProductssearchedProducts  limitedProducts 等计算属性实现了产品列表的无缝过滤、排序和分页。每个计算属性都基于前一个的结果,确保所有操作在状态更改时一致且动态地应用。

过滤:
过滤过程检查是否选择了类别。如果选择了类别,则仅包含属于该类别的产品。

排序:
排序逻辑使用 JavaScript 的 sort 方法:

  • 对于标题,使用 localeCompare 进行字符串比较。
  • 对于价格,执行数值排序。

分页:
slice 方法根据当前页面和选择的限制确定显示哪些产品。例如,如果 limit = 5  page = 2,则显示索引为 5–9 的产品。

结论

感谢你花时间阅读本文。我希望本文能让你清楚地了解如何在 Vue.js 中有效地实现过滤、排序和分页。通过结合 v-model 的数据绑定和 computed 属性的响应式更新,这种方法确保了高效且动态的用户体验。


技术分享

苏南名片

  • 联系人:吴经理
  • 电话:152-1887-1916
  • 邮箱:message@jswusn.com
  • 地址:江苏省苏州市相城区

热门文章

Copyright © 2018-2025 jswusn.com 版权所有

技术支持:苏州网站建设  苏ICP备18036849号