一个基于 Vue 的 GitHub 用户搜索案例

在学习 Vue 组件化开发的过程中,我通过一个 GitHub 用户搜索的小案例,将组件拆分、事件通信、条件渲染以及异步请求等核心知识点串联了起来。虽然功能不复杂,但非常适合作为理解 Vue 应用结构的练习。

整个项目由三个组件组成:App.vueMySearch.vueMyList.vue,采用了典型的"父容器 + 功能组件"的结构设计。


一、根组件 App.vue:只负责组合,不写业务

先看根组件 App.vue

xml 复制代码
<template>
  <div class="container">
    <MySearch/>
    <MyList/>
  </div>
</template>

<script>
import MySearch from './components/MySearch.vue'
import MyList from './components/MyList.vue'

export default {
  name: 'App',
  components: {
    MySearch,
    MyList
  }
}
</script>

可以看到,App.vue 没有任何业务逻辑,只做了三件事:

  1. 引入子组件
  2. 注册子组件
  3. 在模板中组合页面结构

这种写法非常符合 Vue 的设计思想:

根组件只负责"搭架子",具体功能全部下沉到子组件中。


二、MySearch.vue:搜索逻辑与请求发起者

1. 模板结构:输入 + 触发搜索

ini 复制代码
<input
  type="text"
  placeholder="enter the name you search"
  v-model.trim="keyWord"
  @keyup.enter="searchUsers"
/>
<button @click="searchUsers">Search</button>

这里用到了几个非常关键的点:

  • v-model.trim
    自动去掉用户输入的首尾空格,避免无效请求
  • @keyup.enter
    支持回车搜索,提升用户体验
  • 按钮点击和回车复用同一个方法

2. data 定义:只存搜索关键词

javascript 复制代码
data() {
  return {
    keyWord: ''
  }
}

搜索组件的职责非常单一:

👉 只关心"搜什么"与"怎么搜"


3. 核心方法:searchUsers

bash 复制代码
methods: {
  searchUsers() {
    // 搜索前,通知列表进入 loading 状态
    this.$bus.$emit('getSearchData', {
      isFirst: false,
      isLoading: true,
      errMsg: '',
      users: []
    })

    axios.get(`https://api.github.com/search/users?q=${this.keyWord}`).then(
      response => {
        this.$bus.$emit('getSearchData', {
          isLoading: false,
          users: response.data.items
        })
      },
      error => {
        this.$bus.$emit('getSearchData', {
          isLoading: false,
          errMsg: error.message,
          users: []
        })
      }
    )
  }
}

这一段代码是整个项目的逻辑核心,可以总结为三步:

  1. 请求开始前

    • 通知列表组件:

      • 不再是首次进入
      • 正在加载中
  2. 请求成功

    • 将 GitHub 返回的用户数据发送给列表组件
  3. 请求失败

    • 将错误信息传递给列表组件展示

注意:

搜索组件不直接修改列表组件的数据,而是通过事件总线"通知结果"。


三、MyList.vue:统一管理页面展示状态

1. 状态集中管理

yaml 复制代码
data() {
  return {
    info: {
      isFirst: true,
      isLoading: false,
      errMsg: '',
      users: []
    }
  }
}

这里用一个 info 对象统一管理所有 UI 状态,非常关键:

  • 是否首次进入
  • 是否正在加载
  • 是否有错误
  • 是否有数据

👉 状态集中,模板判断才会清晰


2. 模板中的条件渲染

xml 复制代码
<!-- 用户列表 -->
<div
  class="card"
  v-show="info.users.length"
  v-for="user in info.users"
  :key="user.login"
>
  <a :href="user.html_url" target="_blank">
    <img :src="user.avatar_url" style="width: 100px;" />
  </a>
  <p class="card-text">{{ user.login }}</p>
</div>

<h1 v-show="info.isFirst">欢迎!!!</h1>
<h1 v-show="info.isLoading">加载中...</h1>
<h1 v-show="info.errMsg">{{ info.errMsg }}</h1>

通过 v-show 控制不同状态下的界面展示:

  • 初始 → 欢迎提示
  • 搜索中 → 加载中
  • 成功 → 用户列表
  • 失败 → 错误信息

这正是真实业务中最常见的 UI 状态切换场景


3. 接收搜索组件的数据(事件总线)

javascript 复制代码
mounted() {
  this.$bus.$on('getSearchData', datas => {
    this.info = { ...this.info, ...datas }
  })
}

这里的写法非常值得学习:

  • 使用展开运算符合并状态
  • 只更新传过来的字段
  • 保留原有未修改状态

👉 这是一个低耦合、可维护性很强的写法。


四、为什么要用事件总线?

在这个项目中:

  • MySearchMyList兄弟组件
  • 它们之间没有直接的父子 props 关系

使用事件总线的好处是:

  • 不需要层层传 props
  • 组件之间完全解耦
  • 非常适合小型项目和学习阶段

虽然在大型项目中会使用 Vuex / Pinia,但在这里,事件总线是一个非常合适的选择


五、总结

通过这个 GitHub 用户搜索案例,我对 Vue 的理解不再停留在指令和语法层面,而是开始关注:

  • 组件应该如何拆分
  • 数据应该由谁维护
  • 不同状态下页面如何变化
  • 组件之间如何通信才合理
相关推荐
Mr_chiu2 小时前
数据可视化大屏模板:前端开发的效率革命与架构艺术
前端
ZsTs1192 小时前
《2025 AI 自动化新高度:一套代码搞定 iOS、Android 双端,全平台 AutoGLM 部署实战》
前端·人工智能·全栈
命中水2 小时前
从怀疑到离不开:我第一个由 AI 深度参与完成的真实项目复盘
前端·openai
我是ed2 小时前
# Vue3 图片标注插件 AILabel
前端
心在飞扬2 小时前
AI 全栈--reactjs 基础总结
前端
七月十二2 小时前
【TS】虚拟列表无渲染逻辑内核
前端
樊小肆2 小时前
ollmam+langchain.js实现本地大模型简单记忆对话-PostgreSQL版
前端·langchain·aigc
renke33642 小时前
Flutter 2025 模块化与微前端工程体系:从单体到可插拔架构,实现高效协作、独立交付与动态加载的下一代应用结构
前端·flutter·架构
wordbaby2 小时前
配置 Git Hooks:使用 Husky + lint-staged 自动代码检查
前端