Vue.js: 组件基础

最后更新:2026-10-20

Vue 组件是可复用的 Vue 实例——每个组件封装自己的模板、逻辑、样式。Vue 3 的核心架构就是"组件树":根组件(App.vue)→ 页面组件 → 业务组件 → 基础组件(Button/Input)。

掌握组件基础是写大型 Vue 应用的关键一步。本课帮你理解组件的本质、注册方式、命名规范,以及为什么 SFC(单文件组件)是 Vue 推荐的写法。

1. 你将学到


2. 一个 30 页电商后台的代码重复灾难

(1) 痛点:30 个商品卡片,复制粘贴 30 次

Alice 的电商后台需要在多个页面显示 30 个商品卡片。她最初的方案:

HTML
<!-- ❌ 翻车版:30 个商品卡片复制 30 次 -->
<!-- HomeView.vue 中 -->
<div class="product-card">
  <img src="iphone.jpg">
  <h3>iPhone 15 Pro</h3>
  <p>$999</p>
  <button>Add to Cart</button>
</div>
<div class="product-card">
  <img src="macbook.jpg">
  <h3>MacBook Pro</h3>
  <p>$2499</p>
  <button>Add to Cart</button>
</div>
<!-- ... 还有 28 个 ... -->

<!-- 同样的代码又出现在 ProductList.vue、SearchResults.vue、Cart.vue 中 -->

产品经理 Charlie 追加了 5 个新需求:

"Alice,我要把按钮文字从'Add to Cart'改成'Add to Bag'。另外卡片要显示库存状态。还需要做移动端响应式。"

Alice 不得不修改 30+ 个 HTML 文件 × 3 次 = 90 次编辑。这根本无法维护。

(2) Vue 组件解法:1 个 ProductCard.vue,30 处复用

1 个 ProductCard.vue → 30 处复用。改按钮文字只需改 1 处。

(3) 收益

组件化后:


3. 组件是什么?

(1) 定义

组件(Component)是 Vue 中可复用的、有独立逻辑和样式的 UI 单元。每个组件 = 1 个 Vue 实例 + 自己的 data/methods/lifecycle。

100%
graph TB
    A[App.vue<br/>根组件] --> B[HomeView.vue<br/>首页]
    A --> C[AboutView.vue<br/>关于]
    A --> D[DashboardView.vue<br/>仪表盘]
    
    B --> E[ProductCard.vue<br/>商品卡片]
    B --> F[ProductList.vue<br/>商品列表]
    B --> G[FilterBar.vue<br/>筛选栏]
    
    E --> H[Button.vue<br/>按钮]
    E --> I[Badge.vue<br/>徽章]
    
    F --> E
    G --> J[Select.vue<br/>下拉]
    G --> K[Input.vue<br/>输入]
    
    style A fill:#42b883,color:#fff
    style E fill:#42b883,color:#fff
    style H fill:#42b883,color:#fff

(2) 5 大核心特点

特点 说明
可复用 1 个组件可在 N 处使用
封装性 自己的 data / methods / style,互不干扰
可组合 小组件组合成大组件
可测试 单元测试单个组件
可维护 改 1 处 = 改 N 处

(3) 组件 vs 函数

维度 函数 组件
复用单位 代码逻辑 UI + 逻辑
输入 参数 (args) props
输出 return emit
状态 局部变量 data / refs
副作用 无 生命周期钩子

▶ 示例:在 3 个页面复用 ProductCard

▶ 试一试

输出:

TEXT
首页 Featured:iPhone $999 | 库存: 50、MacBook $2499 | 库存: 20
商品列表 All:上面两张 + iPad $599 | 库存: 30
分类 Phones:只有 iPhone $999 | 库存: 50
同一个 ProductCard 组件渲染了 6 次,页面无重复代码

4. 单文件组件 SFC

(1) 什么是 SFC?

SFC(Single File Component)= 1 个 .vue 文件 = 1 个组件。把 template、script、style 集中在一个文件里,便于管理。

(2) SFC 3 部分详解

部分 作用 必填? 语言
<template> HTML 模板 ✅ HTML + Vue 指令
<script setup> JS 逻辑 ✅ JavaScript / TypeScript
<style scoped> CSS 样式 ❌ CSS / SCSS / Less

(3) SFC vs HTML 字符串

JS
// ❌ 不用 SFC(字符串模板,Vue 2 风格)
Vue.component('my-component', {
  template: '<div>{{ msg }}</div>',
  data() { return { msg: 'Hello' } }
})

// ✅ 用 SFC(推荐)
// MyComponent.vue
<template>
  <div>{{ msg }}</div>
</template>
<script setup>
const msg = 'Hello'
</script>

SFC 优势:


5. 组件注册

(1) 全局注册(不推荐大型项目)

JS
// main.js
const { createApp } = Vue
import App from './App.vue'
import ProductCard from './components/ProductCard.vue'

const app = createApp(App)

// 全局注册:所有组件都能用 <ProductCard />
app.component('ProductCard', ProductCard)

app.mount('#app')

(2) 局部注册(推荐)

VUE
<!-- HomeView.vue -->
<script setup>
// 1. 导入
import ProductCard from '@/components/ProductCard.vue'

// 2. 在模板中使用(无需注册,自动可用)
</script>

<template>
  <ProductCard :product="product" />
</template>

(3) 两种注册对比

维度 全局注册 局部注册
可用范围 整个应用 当前组件
代码位置 main.js 组件内 <script setup>
Tree-shaking ❌ 不支持 ✅ 支持
类型提示 弱 强(IDE 自动补全)
推荐度 ⭐⭐ 插件用 ⭐⭐⭐⭐⭐ 业务用

▶ 示例:全局注册 vs 局部注册对比

▶ 试一试

输出:

TEXT
页面上只剩 1 个蓝色按钮"局部注册按钮"(#3b82f6)
#app 里静态写的 h3 和按钮 A/B/C 被 App 的 template 整体替换掉
MyButton 只在 App.components 里局部注册,其他地方直接用会提示组件解析失败

6. 组件命名规范

(1) 3 种命名风格

风格 例子 用法 推荐度
PascalCase ProductCard.vue JS import / 模板中 ⭐⭐⭐⭐⭐
kebab-case product-card.vue 文件名 / kebab-case 模板 ⭐⭐⭐
camelCase productCard.vue 不推荐 ⭐

(2) 模板中使用

(3) 文件夹结构

TEXT
src/
├── components/          # 公共组件(跨页面用)
│   ├── ProductCard.vue
│   ├── Button.vue
│   └── Header.vue
├── views/               # 页面组件(路由用)
│   ├── HomeView.vue
│   ├── ProductListView.vue
│   └── DashboardView.vue
├── composables/         # 组合式函数
│   ├── useAuth.ts
│   └── useFetch.ts
├── router/              # 路由配置
└── App.vue              # 根组件

7. props 和 emit 基础

(1) props:父传子

(2) emit:子传父

(3) props vs emit 速查

方向 API 用途
父 → 子 defineProps() 数据传递
子 → 父 defineEmits() 事件通知

▶ 示例:props 5 大类型 + 验证器

▶ 试一试

输出:

TEXT
管理员 - Alice
年龄: 25 | 状态: 在线
角色: admin | 框架: Vue, React, Angular
邮箱: alice@example.com | 每页: 20

(1) 📊 5 个组件设计原则

原则 说明
单一职责 1 个组件只做 1 件事
可复用 抽象通用 UI(Button/Input/Modal)
可配置 props 控制行为,slot 控制内容
可组合 小组件组合成大组件
易测试 组件独立,便于单元测试

(2) 📊 5 个常见错误

错误 现象 解决
组件名单文件 IDE 报错 用 PascalCase:ProductCard.vue
全局注册太多 bundle 变大 局部注册支持 tree-shaking
组件太大 难维护 拆成小组件
props 直接修改 Vue 警告 props 只读,要改用 emit
忘记 import 组件 模板显示 "unknown component" 检查 <script setup> 中的 import

8. 综合演练:ProductCard 组件全貌

▶ 示例:ProductCard 组件(CDN 多组件演示)

▶ 展开运行示例(65 行)
▶ 试一试

输出:

TEXT
Featured Products
iPhone 15 Pro / $999 / [Add to Cart]
MacBook Pro / $2499 / Only 5 left / [Add to Cart]
Sold Out Item / $99 / [Out of Stock](整卡半透明,按钮禁用不可点)
购物车商品数: 0 → 点前两张卡的按钮变成 1、2…

❓ 常见问题

Q 什么时候用全局注册?
A 只在 2 种情况:(1) 插件式组件(如 Element Plus 的 <el-button>);(2) 整个应用都要用的基础组件(Button/Icon)。业务组件一律局部注册,tree-shaking 才能生效。
Q SFC 必须 3 部分都有吗?
A <template> 和 <script> 必填,<style> 可选。如果组件只有 JS(比如只是封装数据),可以没有 template(用 render 函数)。
Q 组件文件多大合适?
A 建议 < 200 行。超过 300 行考虑拆分。组件越简单越好维护。
Q SFC 用 .vue 后缀,其他工具怎么识别?
A Vite / Webpack / Vue CLI 都默认识别 .vue 后缀。VS Code 装 "Vue - Official" 扩展。
Q 如何测试单个组件?
A 用 Vitest + Vue Test Utils(@vue/test-utils):mount(ProductCard, { props: { product: mockProduct } })。本教程 Phase 4.6 详细介绍。
Q 组件库(Element Plus)怎么用?
A npm 安装 → main.js 全局注册 → 模板中直接用 <el-button>。具体看 Phase 4.7 章节。
Q SFC 和 React JSX 的区别?
A SFC 用 HTML-like 模板(接近 web 标准),React 用 JSX(JS 写 HTML)。SFC 学习曲线平缓,JSX 灵活但需要 build。Vue 3 也支持 JSX(@vitejs/plugin-vue-jsx),但 SFC 是默认推荐。

📖 小节


📝 作业

  1. 基础题(难度⭐) 写一个 Button 组件 src/components/BaseButton.vue:

    • props: text (String), variant (String, 'primary'/'success'/'danger')
    • 3 种 variant 对应 3 种 class
    • emit click 事件
    • 在 HomeView.vue 中使用 3 个不同 variant
  2. 进阶题(难度⭐⭐) 写一个商品列表 + 卡片系统:

    • ProductCard.vue:单个商品卡片(props: product, emit: add-to-cart)
    • ProductList.vue:商品列表容器(v-for + 过滤)
    • App.vue:根组件,传数据 + 处理 add-to-cart 事件
    • 3 个文件 3 个组件嵌套使用
  3. 挑战题(难度⭐⭐⭐) 实现一个完整的"用户卡片"组件系统:

    1. Avatar.vue:头像组件(支持 url / initials 两种模式)
    2. UserCard.vue:用户卡片(用 Avatar)
    3. UserList.vue:用户列表(用 UserCard)
    4. App.vue:根组件(数据 + 交互)
    5. 5 个 props(avatar/name/email/role/active)
    6. 3 个 emit 事件(edit/delete/select)
    7. scoped 样式隔离
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏