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

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

整个项目由三个组件组成:App.vue、MySearch.vue 和 MyList.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 }
  })
}

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

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

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


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

在这个项目中:

  • MySearch 和 MyList 是兄弟组件
  • 它们之间没有直接的父子 props 关系

使用事件总线的好处是:

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

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


五、总结

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

  • 组件应该如何拆分
  • 数据应该由谁维护
  • 不同状态下页面如何变化
  • 组件之间如何通信才合理
相关推荐
jktantan5 小时前
Vue keep-alive 切标签后滚动位置回顶?因为 onDeactivated 里读到的 scrollTop 是 0
前端·javascript·vue.js
2601_962885726 小时前
quotes 字段全解:涨跌幅、振幅、换手率的口径与单位
java·前端·spring
excel6 小时前
encodeURI 和 encodeURIComponent 有什么区别?
前端
前端精髓6 小时前
调用openrouter的免费API模型
java·前端·javascript
赛博守夜人6 小时前
AI安全攻防与治理之十四:AI 安全测试不能脱离传统安全,Web、API、移动端与模型攻击面的组合拳
前端·人工智能·安全
开开心心_Every6 小时前
快速隐藏程序工具自定义快捷键一键切换
java·开发语言·前端·spring·智能手机·word·intellij-idea
networknomad7 小时前
OpenReplay:自己搭的会话回放工具,给前端排错用的
前端·其他
缓慢更新8 小时前
AI 辅助生成单测:从接口定义到用例的流水线
java·服务器·前端·网络·数据库·人工智能
陈随易8 小时前
AI 运维服务器,安全又可靠的方法
前端·后端·程序员
风骏时光牛马9 小时前
AI稳定性治理体系构建与疑难问题深度分析
前端