50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | LiveUserFilter(实时用户过滤组件)
📅 我们继续 50 个小项目挑战!—— LiveUserFilter
组件
仓库地址:https://github.com/SunACong/50-vue-projects
项目预览地址:https://50-vue-projects.vercel.app/
🔍 使用 Vue 3 和 Tailwind CSS 构建实时用户搜索过滤器
欢迎来到本篇教程!今天我们将使用 Vue 3 的 <script setup>
语法结合 Tailwind CSS 来创建一个功能强大且视觉美观的实时用户搜索过滤组件。这个组件能够从远程 API 获取用户数据,并根据用户输入的关键词实时过滤并展示结果,非常适合用于构建用户目录、联系人列表或任何需要搜索功能的界面。
让我们开始吧!🚀
📝 应用目标
- 使用 Vue 3 Composition API 管理异步数据获取与状态
- 实现基于姓名和/或位置的实时用户搜索过滤
- 展示加载状态和无结果状态
- 使用 Tailwind CSS 快速构建现代化的 UI 组件
- 优化用户体验,提供流畅的搜索反馈
🔧 技术实现点
技术点 | 描述 |
---|---|
Vue 3 <script setup> | 使用 ref , onMounted , computed 管理状态、生命周期和计算属性 |
async/await | 异步获取远程用户数据 |
computed 属性 | 响应式地过滤用户列表 |
v-model 双向绑定 | 绑定搜索输入框的值 |
v-for 渲环渲染 | 动态生成用户列表项 |
v-if 条件渲染 | 根据状态显示加载中、用户列表或无结果提示 |
Tailwind CSS | 快速实现响应式布局与视觉样式 |
📚 常量和路由定义
虽然本示例是一个独立的功能组件,但理解其核心配置和数据流对于维护和扩展至关重要:
常量/配置 | 值/说明 |
---|---|
API_URL | https://randomuser.me/api?results=50 - 用于获取随机用户数据的公共 API |
resultsCount | 50 - 一次请求获取的用户数量 |
searchTerm (响应式) | 存储用户输入的搜索关键词 |
users (响应式) | 存储从 API 获取的原始用户数据数组 |
filteredUsers (计算属性) | 根据 searchTerm 过滤后的用户列表 |
isLoading (响应式) | 控制加载状态显示的布尔值 |
💡 提示:在实际项目中,这些配置(如 API 地址)可以定义为常量或从环境变量中读取,便于统一管理和环境切换。
🖌️ 组件实现
🎨 模板结构 <template>
<template><divclass="flex min-h-screen items-center justify-center overflow-hidden bg-gray-100 font-sans"><div class="w-72 overflow-hidden rounded-lg bg-white shadow-md"><!-- 头部搜索区域 --><div class="bg-blue-600 p-6 text-white"><h4 class="mb-1 text-xl font-bold">Live User Filter</h4><small class="opacity-80">Search by name and/or location</small><inputv-model="searchTerm"@input="filterUsers"class="mt-3 w-full rounded-full bg-blue-800/50 px-4 py-2 text-sm text-white focus:outline-none"placeholder="Search" /></div><!-- 用户列表区域 --><ul class="max-h-96 overflow-y-auto"><!-- 加载状态 --><li v-if="isLoading" class="p-6 text-center"><h3>Loading...</h3></li><!-- 过滤后的用户列表 --><liv-for="user in filteredUsers":key="user.login.uuid"class="flex items-center border-b border-gray-100 p-4"><img:src="user.picture.large":alt="`${user.name.first} ${user.name.last}`"class="h-12 w-12 rounded-full object-cover" /><div class="ml-3"><h4 class="font-medium text-gray-900">{{ user.name.first }} {{ user.name.last }}</h4><p class="text-xs text-gray-500">{{ user.location.city }}, {{ user.location.country }}</p></div></li><!-- 无结果状态 --><liv-if="!isLoading && filteredUsers.length === 0"class="p-6 text-center text-gray-500">No users found</li></ul></div></div>
</template>
模板部分清晰地分为两个主要区域:
- 头部搜索区域:包含标题、说明和一个搜索输入框。输入框使用
v-model
绑定到searchTerm
,并监听@input
事件(尽管实际过滤由computed
属性驱动)。 - 用户列表区域:一个可滚动的列表,根据
isLoading
和filteredUsers
的状态动态显示:isLoading
为true
时显示“Loading…”。- 否则,使用
v-for
遍历filteredUsers
显示用户头像、姓名和位置。 - 当
filteredUsers
为空且非加载状态时,显示“No users found”。
💻 脚本逻辑 <script setup>
<script setup>import { ref, onMounted, computed } from 'vue'// 响应式状态const users = ref([])const searchTerm = ref('')const isLoading = ref(true)// 获取用户数据const fetchUsers = async () => {try {isLoading.value = trueconst response = await fetch('https://randomuser.me/api?results=50')const data = await response.json()users.value = data.results} catch (error) {console.error('Error fetching users:', error)} finally {isLoading.value = false}}// 过滤用户数据const filteredUsers = computed(() => {if (!searchTerm.value) return users.valueconst term = searchTerm.value.toLowerCase()return users.value.filter((user) => {const fullName = `${user.name.first} ${user.name.last}`.toLowerCase()const location = `${user.location.city} ${user.location.country}`.toLowerCase()return fullName.includes(term) || location.includes(term)})})// 组件挂载时获取数据onMounted(() => {fetchUsers()})// 暴露给模板的方法const filterUsers = () => {} // 仅为了模板中绑定,实际过滤通过computed完成
</script>
🎉 关键功能解析
✅ 状态定义
users
存储用户数据,searchTerm
存储搜索关键词,isLoading
控制加载状态。
✅ 数据获取
fetchUsers
函数使用 fetch
API 异步获取 50 个随机用户数据,并在成功或失败后更新 isLoading
状态。
✅ 数据过滤
filteredUsers
是一个 computed
属性,它会自动响应 searchTerm
和 users
的变化。当 searchTerm
为空时返回所有用户;否则,将用户姓名和位置转换为小写,并检查是否包含搜索关键词(也转为小写),返回匹配的用户数组。
✅ 生命周期
onMounted
钩子确保组件挂载后立即调用 fetchUsers
获取数据。
✅ 方法占位
filterUsers
函数虽然在模板中被绑定,但实际过滤逻辑由 computed
属性 filteredUsers
完成,因此该函数为空。v-model
的更新足以触发 computed
的重新计算。
🎨 Tailwind CSS 样式重点
类名 | 作用 |
---|---|
flex | 启用 Flexbox 布局 |
min-h-screen | 设置最小高度为视口高度,确保内容居中 |
items-center / justify-center | 在主轴和交叉轴上居中对齐 |
overflow-hidden | 隐藏溢出内容 |
bg-gray-100 | 设置浅灰色背景 |
font-sans | 使用无衬线字体 |
w-72 | 设置固定宽度(18rem) |
rounded-lg | 添加中等圆角 |
bg-white | 白色背景 |
shadow-md | 添加中等阴影,提升立体感 |
bg-blue-600 | 搜索区域深蓝色背景 |
p-6 | 内边距 |
text-white | 白色文字 |
mb-1 | 下边距 |
text-xl / text-sm | 字体大小 |
font-bold / font-medium | 字体粗细 |
opacity-80 | 降低不透明度 |
mt-3 | 上边距 |
rounded-full | 完全圆形(圆角) |
bg-blue-800/50 | 深蓝色背景,50% 透明度(使用 Tailwind CSS 新的透明度语法) |
px-4 py-2 | 水平/垂直内边距 |
focus:outline-none | 移除聚焦轮廓 |
max-h-96 | 设置最大高度(24rem),创建滚动区域 |
overflow-y-auto | Y轴自动出现滚动条 |
border-b border-gray-100 | 底部浅灰色边框,分隔列表项 |
p-4 | 列表项内边距 |
flex items-center | 水平 Flex 布局,垂直居中 |
h-12 w-12 | 头像固定大小 |
rounded-full | 头像圆形 |
object-cover | 图片填充容器并保持比例 |
ml-3 | 左边距 |
text-gray-900 / text-gray-500 | 文字颜色 |
text-xs | 小号文字 |
📁 常量定义 + 组件路由
constants/index.js
添加组件预览常量:
{id: 42,title: 'LiveUserFilter',image: 'https://50projects50days.com/img/projects-img/42-live-user-filter.png',link: 'LiveUserFilter',},
router/index.js
中添加路由选项:
{path: '/LiveUserFilter',name: 'LiveUserFilter',component: () => import('@/projects/LiveUserFilter.vue'),},
🏁 总结
在这篇教程中,我们成功构建了一个功能完整、界面美观的实时用户搜索过滤组件。我们利用了 Vue 3 的响应式系统(特别是 computed
属性)来实现高效的过滤逻辑,并通过 Tailwind CSS 快速搭建了现代化的 UI。
想要让你的用户过滤器更加强大?试试这些扩展功能:
- ✅ 防抖搜索:如果数据量巨大或 API 有调用限制,可以为搜索输入添加防抖(debounce)功能,避免过于频繁的计算或 API 调用。
- ✅ 高级筛选:除了搜索框,添加下拉菜单或复选框进行更精确的筛选(如按国家、性别等)。
- ✅ 分页:当用户数据量非常大时,实现分页功能。
- ✅ 缓存机制:缓存已获取的数据,避免重复请求。
- ✅ 错误处理 UI:除了
console.error
,在界面上显示友好的错误提示。 - ✅ 键盘导航:支持使用键盘上下键在用户列表中导航。
👉 下一篇,我们将完成FeedbackUiDesign
组件,实现了一个好玩儿的好评反馈组件。🚀
感谢阅读,欢迎点赞、收藏和分享 😊