Vue-github 用户搜索案例

一、前言

还在学 Vue 的基础语法?学完 v-modelv-forv-if 却不知道如何整合成一个完整项目?

别担心!本文带你从零开始,用 2 小时完成一个真实的 Vue 项目 ------ GitHub 用户搜索系统

✅ 你将学到:

  • 如何使用 axios 发送网络请求
  • 组件化开发与父子/兄弟组件通信
  • 使用 全局事件总线(Global Event Bus) 实现跨组件数据传递
  • 处理加载状态与错误提示
  • 项目结构组织与代码规范

💡 本项目不依赖 Vue Router 或 Vuex,纯原生 Vue + Axios,适合初学者快速上手!

二、项目演示

功能说明:

  • 输入用户名关键词,点击搜索
  • 显示加载中动画
  • 展示用户头像、用户名、主页链接
  • 支持点击头像跳转到 GitHub 主页
  • 搜索失败时显示错误信息
  • 初始页面显示欢迎语

三、涉及知识点

知识点 说明
Vue 基础 模板语法、v-model、v-for、v-show、生命周期
Axios 发送 HTTP 请求获取 GitHub 用户数据
全局事件总线 实现 SearchList 组件通信
组件化开发 拆分为 App.vueSearch.vueList.vue
GitHub Open API 免费接口,无需认证

四、项目准备

1. 创建 Vue 项目

bash 复制代码
# 使用 Vue CLI(Vue 2)
vue create github-search

# 或使用 Vite(Vue 3)
npm create vue@latest github-search

本文以 Vue 2 + Vue CLI 为例,Vue 3 写法类似。


2. 安装依赖

bash 复制代码
# 安装 axios(发送请求)
npm install axios

# 可选:安装 bootstrap 美化界面
npm install bootstrap

3. 引入 Bootstrap(可选)

main.js 中引入:

javascript 复制代码
import 'bootstrap/dist/css/bootstrap.css'

五、项目结构

复制代码
src/
├── App.vue
├── components/
│   ├── Search.vue      # 搜索组件
│   └── List.vue        # 列表展示组件
├── eventBus.js         # 全局事件总线
└── main.js

六、核心代码实现

1. 创建全局事件总线 eventBus.js

javascript 复制代码
// src/eventBus.js
import Vue from 'vue'
export default new Vue()

📌 作用:作为"中转站",实现兄弟组件通信。


2. App.vue ------ 父组件(整合子组件)

html 复制代码
<template>
  <div class="container">
    <h1 class="text-center my-4">🔍 GitHub 用户搜索</h1>
    <Search />
    <List />
  </div>
</template>

<script>
import Search from './components/Search.vue'
import List from './components/List.vue'

export default {
  name: 'App',
  components: {
    Search,
    List
  }
}
</script>

<style>
.container {
  max-width: 900px;
  margin: 0 auto;
  padding: 20px;
}
</style>

3. Search.vue ------ 搜索组件(发送请求)

html 复制代码
<template>
  <section class="jumbotron">
    <h3 class="jumbotron-heading">Search GitHub Users</h3>
    <div>
      <input
        type="text"
        placeholder="请输入要搜索的用户名"
        v-model="keyWord"
        @keyup.enter="searchUsers"
      />
      &nbsp;
      <button @click="searchUsers" class="btn btn-primary">搜索</button>
    </div>
  </section>
</template>

<script>
import axios from 'axios'
import eventBus from '../eventBus'

export default {
  name: 'Search',
  data() {
    return {
      keyWord: '' // 搜索关键词
    }
  },
  methods: {
    searchUsers() {
      // 1. 搜索前:通知 List 组件更新状态(加载中)
      eventBus.$emit('updateList', {
        isFirst: false,
        isLoading: true,
        errMsg: '',
        users: []
      })

      // 2. 发送请求
      const url = `https://api.github.com/search/users?q=${this.keyWord}`
      axios.get(url)
        .then(response => {
          // 请求成功
          const users = response.data.items
          eventBus.$emit('updateList', {
            isLoading: false,
            users
          })
        })
        .catch(error => {
          // 请求失败
          eventBus.$emit('updateList', {
            isLoading: false,
            errMsg: error.message
          })
        })
    }
  }
}
</script>

<style scoped>
.jumbotron {
  background-color: #f8f9fa;
  padding: 2rem;
  border-radius: 10px;
  margin-bottom: 20px;
}
input {
  width: 60%;
  padding: 8px;
  border: 1px solid #ddd;
  border-radius: 4px;
}
</style>

4. List.vue ------ 列表组件(展示数据)

html 复制代码
<template>
  <div class="row">
    <!-- 用户列表 -->
    <div
      v-for="user in info.users"
      :key="user.login"
      class="card col-lg-3 col-md-4 col-sm-6 p-0"
    >
      <a :href="user.html_url" target="_blank">
        <img :src="user.avatar_url" alt="头像" style="width: 100%; height: 200px; object-fit: cover;">
      </a>
      <p class="card-text text-center">{{ user.login }}</p>
    </div>

    <!-- 欢迎语 -->
    <h2 v-show="info.isFirst" class="welcome text-center">欢迎使用 GitHub 搜索系统!</h2>

    <!-- 加载中 -->
    <h2 v-show="info.isLoading" class="text-center">🔍 搜索中,请稍候...</h2>

    <!-- 错误信息 -->
    <h2 v-show="info.errMsg" class="text-danger text-center">❌ {{ info.errMsg }}</h2>
  </div>
</template>

<script>
import eventBus from '../eventBus'

export default {
  name: 'List',
  data() {
    return {
      info: {
        isFirst: true,     // 是否为初始状态
        isLoading: false,  // 是否正在加载
        errMsg: '',        // 错误信息
        users: []          // 用户列表
      }
    }
  },
  mounted() {
    // 接收 Search 组件发送的数据
    eventBus.$on('updateList', (data) => {
      // 合并对象
      this.info = { ...this.info, ...data }
    })
  },
  // 组件销毁前解绑事件,防止内存泄漏
  beforeDestroy() {
    eventBus.$off('updateList')
  }
}
</script>

<style scoped>
.row {
  margin: 0;
}
.card {
  margin-bottom: 1rem;
  text-align: center;
  border: none;
}
.card img {
  border-radius: 8px;
}
.welcome {
  color: #666;
  font-size: 1.2rem;
  margin-top: 50px;
}
</style>

七、运行项目

bash 复制代码
npm run serve

访问 http://localhost:8080,输入用户名如 tomjohn,即可看到搜索结果!

八、常见问题与优化建议

❌ 问题 1:事件总线未解绑导致内存泄漏?

解决 :在 beforeDestroy 中使用 $off 解绑:

javascript 复制代码
beforeDestroy() {
  eventBus.$off('updateList')
}

❌ 问题 2:输入框回车搜索?

✅ 已在 v-model 上添加 @keyup.enter="searchUsers",支持回车触发。


✅ 优化建议

优化点 说明
防抖处理 频繁输入时可添加 lodash.debounce 防抖
图片懒加载 安装 vue-lazyload 插件,提升性能
错误重试 增加重试按钮,提升用户体验
样式美化 使用 Element UI / Ant Design Vue 提升 UI

九、扩展:Vue 3 写法差异(Composition API)

如果你使用 Vue 3 + setupList.vue 可改为:

javascript 复制代码
import { onMounted, onBeforeUnmount } from 'vue'

export default {
  setup() {
    const info = reactive({
      isFirst: true,
      isLoading: false,
      errMsg: '',
      users: []
    })

    onMounted(() => {
      eventBus.on('updateList', (data) => {
        Object.assign(info, data)
      })
    })

    onBeforeUnmount(() => {
      eventBus.off('updateList')
    })

    return { info }
  }
}

十、总结

组件 职责
App.vue 整合组件,搭建页面结构
Search.vue 用户输入、发送请求、通知状态
List.vue 接收数据、展示列表、处理 UI 状态
eventBus.js 兄弟组件通信桥梁

项目收获

  • 掌握了 Vue 组件化开发流程
  • 学会了使用 Axios 请求数据
  • 理解了全局事件总线的通信机制
  • 提升了错误处理与用户体验意识

十一、结语

感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!

相关推荐
yong99907 小时前
响应式布局新利器:CSS Grid 的 grid-template-areas 实战
前端·css
咖啡の猫7 小时前
Vue过度与动画
前端·javascript·vue.js
IT_陈寒7 小时前
Python数据处理速度慢?5行代码让你的Pandas提速300% 🚀
前端·人工智能·后端
蒜香拿铁7 小时前
Angular【起步】
前端·javascript·angular.js
护国神蛙7 小时前
HTTP 重定向踩坑实录:307、301、308 问题排查全指南
前端·网络协议
初心丨哈士奇7 小时前
前端Vibe Coding探索:Cursor+MCP打造沉浸式开发流(使用MCP与Cursor Rules让Vibe Coding更快速与精准)
前端·人工智能
Hilaku7 小时前
前端开发,真的有必要学Docker吗?
前端·javascript·docker
安迪西嵌入式7 小时前
数据平滑处理算法03——中心移动平均
java·前端·算法
掘金安东尼7 小时前
🧭 前端周刊第428期(2025年10月28日–11月3日)
前端·github