LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

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

admin
2024年12月31日 21:19 本文热度 1155

在本文中,我将分享如何在 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://fakestoreapi.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://fakestoreapi.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 属性的响应式更新,这种方法确保了高效且动态的用户体验。

如果你有任何建议、问题或反馈,请随时分享!我始终乐于讨论并愿意提供帮助。
GitHub: https://github.com/rfkyalf/vue-api-handling.git

我始终对连接和协作开发 Web 项目充满热情。你可以访问我的作品集网站了解更多关于我的工作和过往项目的信息:https://www.rifkyalfarez.my.id


原文地址:https://dev.to/rifkyalfarez/how-to-fetch-api-and-implement-filtering-sorting-and-pagination-in-vuejs-1cpg


该文章在 2025/1/2 17:20:30 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved