news 2026/2/28 18:17:44

Vue.js 组件全面学习指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Vue.js 组件全面学习指南

1 学习指南

1.1 学习目标

  • 认知层级

    目标描述

    对应章节

    了解

    掌握组件化开发思想与核心价值

    2.1 组件化思想

    掌握

    组件注册、父子通信基础用法

    3.1-3.2、4.1-4.2

    熟练

    高级组件特性与复杂场景应用

    4.3-4.5、6.0

    应用

    独立完成中型项目组件拆分与实现

    5.0 实战案例

1.2 前置知识

  • 掌握 Vue 基础语法(模板、指令、响应式数据)
  • 了解 ES6 + 语法(解构、箭头函数、模块化)
  • 具备 HTML/CSS 基础

2 组件的简介

组件是 Vue.js 的核心概念,是可复用的 Vue 实例,相当于页面的 “功能积木”。将复杂页面拆分为独立、可复用的组件,能实现:

  • 代码模块化:单个组件专注单一功能,维护成本降低
  • 复用性提升:同一组件可在多个页面重复使用
  • 协作高效:多人开发时可并行开发不同组件

组件的本质是 “独立的视图 + 逻辑单元”,类比现实中的 “乐高积木”—— 每个积木有固定功能(比如轮子、车身),组合起来形成完整模型(应用)。

3 组件的使用

3.1 组件注册方式

3.1.1 全局注册(全局可用)
// main.js import { createApp } from 'vue' import App from './App.vue' import GlobalComponent from './components/GlobalComponent.vue' const app = createApp(App) // 全局注册:所有组件均可直接使用 app.component('GlobalComponent', GlobalComponent) app.mount('#app')
3.1.2 局部注册(仅当前组件可用)
<LocalComponent /> 局部组件只能在当前父组件使用 --> > // 局部注册:导入即注册,无需额外配置 import LocalComponent from './LocalComponent.vue' </script>

3.2 组件使用规范

注册方式对比

注册方式

适用场景

优点

缺点

最佳实践

全局注册

通用基础组件(如 Button/Icon)

全局可直接使用,无需导入

增加打包体积,未使用组件无法 Tree-Shaking

注册不超过 5 个核心基础组件

局部注册

业务组件(如 TodoInput/TodoItem)

按需导入,减小打包体积

需手动导入后使用

业务模块优先使用,配合自动导入插件

  • 组件名建议使用kebab-case(短横线分隔),如todo-item
  • 单文件组件(SFC)统一后缀为.vue,包含<template>/<script>/` 三部分
  • 样式隔离:在标签添加scoped` 属性,避免样式污染

4 组件之间的通信

组件通信是组件协作的核心,Vue3 提供了多种通信方案,重点关注父子组件通信。

4.1 父组件向子组件通信(已完成)

核心方案:Props 传递(单向数据流:父传子,子不可直接修改 props)

Parent.vue --> > :username="userName" :age="userAge" /> <script setup> import { ref } from 'vue' import ChildComponent from './ChildComponent.vue' const userName = ref('张三') const userAge = ref(18) </script> 组件 ChildComponent.vue --> <template> <p>姓名:{{ username }}</p> <p>年龄:{{ age }}</p> > // 定义props并指定类型、默认值、必填项 const props = defineProps({ username: { type: String, required: true }, age: { type: Number, default: 18 // 未传递时使用默认值 } }) </script>

4.2 子组件向父组件通信

核心方案:自定义事件($emit),类比 “儿子向父亲汇报情况”,子组件触发事件,父组件监听并处理。

4.2.1 基础用法(3 步实现)
  1. 子组件声明可触发的事件(defineEmits)
  1. 子组件触发事件(emit('事件名', 数据))
  1. 父组件监听事件(@事件名="处理函数")
<!-- 子组件 ChildComponent.vue --> ="sendMessage">向父组件发送消息 ="inputValue" placeholder="输入要传递的内容" /> <script setup> import { ref } from 'vue' // 1. 声明可触发的事件(支持多个,数组形式) const emit = defineEmits(['message-sent', 'value-change']) const inputValue = ref('') // 2. 触发事件:第一个参数是事件名,后续是传递的数据 function sendMessage() { emit('message-sent', '子组件问候:你好,父组件!') // 传递字符串 emit('value-change', inputValue.value) // 传递输入框值 } .vue --> @message-sent="handleMessage" @value-change="handleValueChange" /> 的消息:{{ parentMessage }}</p> <p>父组件接收的输入值:{{ parentValue }}</p> > import { ref } from 'vue' import ChildComponent from './ChildComponent.vue' const parentMessage = ref('') const parentValue = ref('') // 3. 监听事件并处理数据 function handleMessage(msg) { parentMessage.value = msg } function handleValueChange(val) { parentValue.value = val } #### 4.2.2 进阶用法:v-model双向绑定(语法糖) `v-model`是`props + emit`的组合,适用于“父子组件数据同步”场景(如自定义输入框): ```vue <!-- 子组件 CustomInput.vue --> :value="modelValue" @input="emit('update:modelValue', $event.target.value)" /> 接收父组件v-model传递的value(默认名modelValue) defineProps(['modelValue']) // 声明更新事件(固定格式:update:modelValue) const emit = defineEmits(['update:modelValue']) .vue --> :Value="searchText" @update:modelValue="searchText = $event" /> --> v-model="searchText" /> 搜索内容:{{ searchText }}</p> > import { ref } from 'vue' import CustomInput from './CustomInput.vue' const searchText = ref('')
4.2.2 注意事项
  • 事件名建议使用kebab-case(短横线分隔),与组件名规范一致
  • 可传递任意类型数据(字符串、对象、数组等)
  • 子组件不可直接修改父组件数据,必须通过 “触发事件” 让父组件自行修改(遵循单向数据流)

5 组件实战案例:TodoList 任务清单

5.1 案例需求

实现一个简易 TodoList,包含功能:

  • 父组件管理任务列表(数据存储)
  • 子组件 1(TodoInput):输入任务并添加到列表(子传父)
  • 子组件 2(TodoItem):展示单个任务,支持删除功能(子传父)
  • 父组件向子组件传递任务数据(父传子)

5.2 代码实现

5.2.1 目录结构
src/ ├── components/ │ ├── TodoInput.vue // 任务输入组件(子) │ └── TodoItem.vue // 单个任务组件(子) └── App.vue // 父组件(任务列表管理)
5.2.2 子组件 1:TodoInput.vue(添加任务)
-input"> v-model="newTodo" placeholder="请输入新任务" @keyup.enter="addTodo" /> click="addTodo">添加</button> </div> { ref } from 'vue' const emit = defineEmits(['add-todo']) // 声明添加事件 const newTodo = ref('') function addTodo() { if (newTodo.value.trim()) { // 传递任务对象给父组件 emit('add-todo', { id: Date.now(), // 用时间戳作为唯一ID content: newTodo.value, done: false }) newTodo.value = '' // 清空输入框 } } <style scoped> .todo-input { margin: 20px 0; display: flex; gap: 10px; } input { flex: 1; padding: 8px; } button { padding: 8px 16px; background: #42b983; color: white; border: none; border-radius: 4px; cursor: pointer; } </style>
5.2.3 子组件 2:TodoItem.vue(单个任务)
<template> class="todo-item"> done: todo.done }" @click="toggleDone"> {{ todo.content }} ="deleteTodo">删除</button> > <script setup> const props = defineProps({ todo: { type: Object, required: true, // 验证todo对象结构 validator: (value) => { return 'id' in value && 'content' in value && 'done' in value } } }) const emit = defineEmits(['delete-todo', 'toggle-done']) // 触发删除事件(传递任务ID) function deleteTodo() { emit('delete-todo', props.todo.id) } // 触发状态切换事件 function toggleDone() { emit('toggle-done', props.todo.id) } </script> oped> .todo-item { display: flex; justify-content: space-between; align-items: center; padding: 8px; border-bottom: 1px solid #eee; } .done { text-decoration: line-through; color: #999; } button { background: #ff4444; color: white; border: none; border-radius: 4px; padding: 4px 8px; cursor: pointer; }
5.2.4 父组件:App.vue(核心管理)
<template> 1>TodoList 任务清单 :输入任务(子传父:添加任务) --> add-todo="handleAddTodo" /> 子组件2:展示任务(父传子:任务数据;子传父:删除/切换) --> todo-list"> <TodoItem v-for="todo in todoList" :key="todo.id" :todo="todo" @delete-todo="handleDeleteTodo" @toggle-done="handleToggleDone" /> </div> > import { ref } from 'vue' import TodoInput from './components/TodoInput.vue' import TodoItem from './components/TodoItem.vue' // 父组件维护任务列表(响应式数据) const todoList = ref([ { id: 1, content: '学习Vue组件通信', done: false }, { id: 2, content: '完成实战案例', done: false } ]) // 处理添加任务(接收子组件传递的任务对象) function handleAddTodo(newTodo) { todoList.value.push(newTodo) } // 处理删除任务(接收子组件传递的任务ID) function handleDeleteTodo(todoId) { todoList.value = todoList.value.filter(todo => todo.id !== todoId) } // 处理切换任务状态(接收子组件传递的任务ID) function handleToggleDone(todoId) { const todo = todoList.value.find(todo => todo.id === todoId) if (todo) todo.done = !todo.done } </script> scoped> h1 { color: #333; } .todo-list { border: 1px solid #eee; border-radius: 4px; padding: 10px; }

5.3 案例效果

- 输入任务内容,点击“添加”或按回车,任务会添加到列表(子传父)

- 点击任务文本,切换完成状态(子传父触发状态更新)

- 点击“删除”按钮,移除对应任务(子传父触发删除)

- 父组件通过props将任务数据传递给`TodoItem`组件(父传子)

5.4 案例核心知识点总结

1. 组件拆分:按功能拆分为“输入组件”“列表项组件”“父组件”,职责单一

2. 父传子:通过props传递任务数据、配置项

3. 子传父:通过自定义事件传递操作(添加/删除/切换)和数据(任务ID/对象)

4. 响应式数据:父组件维护核心数据,子组件仅负责展示和触发事件

如果需要补充某部分细节(如 TypeScript 支持、组件复用技巧),或想扩展其他实战案例(如购物车、表单组件),可以随时告诉我!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/28 16:11:55

SchoolDash Alpha冲刺随笔3 - Day 5

SchoolDash Alpha冲刺随笔3 - Day 5 课程与作业信息 所属课程&#xff1a;软件工程实践 作业要求来源&#xff1a;第五次作业——Alpha冲刺 本篇目标&#xff1a;记录冲刺第5天进度 项目燃尽图&#xff08;Burn-up Chart&#xff09; 当前冲刺总Story Point&#xff1a;50 …

作者头像 李华
网站建设 2026/2/27 23:45:32

快速提取Oracle AWR报告指南

1. 当前连接实例的AWR报告提取 注意&#xff1a;中间没有任何空格&#xff0c;需要在数据库服务器上Oracle用户登陆 ?/rdbms/admin/awrrpt2. 输入格式 Enter value for report_type: htmlType Specified: html注意&#xff1a;这里直接回…

作者头像 李华
网站建设 2026/2/26 15:35:41

SGMICRO圣邦微 SGM2019-1.8YC5G/TR SC70-5 线性稳压器(LDO)

特性工作输入电压范围&#xff1a;2.5V至5.5V固定输出电压&#xff1a;1.2V、1.5V、1.8V、2.5V、2.6V、2.8V、2.85V、3.0V、3.3V可调输出电压范围&#xff1a;1.2V至5.0V输出电压精度&#xff1a;25C时为2.5%低输出噪声&#xff1a;30μV_RMS&#xff08;典型值&#xff09;低压…

作者头像 李华
网站建设 2026/2/28 21:22:07

Python 爬虫实战:Scrapy 中间件自定义开发

前言 Scrapy 框架的高扩展性核心体现在其模块化的组件设计&#xff0c;而中间件&#xff08;Middleware&#xff09;是连接引擎&#xff08;Engine&#xff09;与其他核心组件&#xff08;下载器、爬虫、响应处理&#xff09;的关键桥梁。无论是应对反爬机制&#xff08;如 UA…

作者头像 李华
网站建设 2026/2/26 10:15:35

39. UVM Factory Override

UVM Factory&#xff1a;验证平台的"智能生产线" 我们要学习UVM中最强大、最核心的特性之一&#xff1a;Factory机制。这就像是一个智能的生产线&#xff0c;可以根据需求动态更换生产的产品类型&#xff0c;而不需要修改生产线本身。 &#x1f3af; 一句话理解UVM F…

作者头像 李华