Vue 组件详解笔记:从概念到实战(初学者必学)
Vue 组件详解笔记:从概念到实战(初学者必学)
各位小伙伴,组件是 Vue 的核心灵魂!学会组件,我们才能真正用 Vue 做组件化开发 —— 把页面拆成一个个可复用的 “积木块”,比如导航栏、卡片、按钮,既方便维护又能提高效率。这篇笔记会带我们从 “什么是组件” 开始,一步步学会创建、导入、使用组件,还有最关键的 “父组件给子组件传值”,全程带代码示例和易错点提醒,跟着学完就能上手写自己的组件啦!
一、先搞懂:什么是 Vue 组件?
·我们先从最基础的概念入手,搞清楚 “组件到底是什么”“为什么要用组件”。
1.1 组件的本质:可复用的 Vue 实例
Vue 组件就是一个可复用的、封装了模板(HTML)、逻辑(JS)和样式(CSS)的 Vue 实例,每个以.vue为后缀的文件,都是一个独立的组件。
比如我们之前见过的HelloWorld.vue、HomeView.vue,本质上都是组件 —— 它们可以被多次引用(比如在不同页面用同一个按钮组件),也可以嵌套(比如首页组件里包含导航栏组件)。
1.2 组件的 3 大组成部分(.vue 文件结构)
每个.vue组件都由 3 个顶层代码块构成,各司其职,缺一不可的只有<template>:
| 代码块 | 作用说明 | 必选 / 可选 | 关键注意点 |
|---|---|---|---|
<template> |
定义组件的 HTML 结构,负责 “显示什么” | 必选 | Vue 2 中必须有一个根元素(比如最外层包一个<div>);Vue 3 支持多根元素,但推荐保留单根,避免混乱 |
<script> |
写组件的 JS 逻辑,负责 “做什么”(定义数据、方法、接收父组件传值等) | 可选 | 用export default导出组件,让其他组件能导入使用 |
<style> |
定义组件的 CSS 样式,负责 “长什么样” | 可选 | 加scoped属性后,样式只作用于当前组件(不会污染其他组件,强烈推荐加) |
基础组件示例(PersonDetail.vue):
<template>
<!-- 模板:单根元素包裹,显示内容 -->
<div class="person-detail">
<h3>我是个人信息组件</h3>
</div>
</template>
<script>
// 逻辑:导出组件,定义组件名
import { defineComponent } from 'vue'
export default defineComponent({
name: 'PersonDetail' // 组件名(调试时用,推荐和文件名一致)
})
</script>
<style scoped>
// 样式:scoped确保只影响当前组件
.person-detail {
color: #333;
margin: 20px 0;
}
</style>
1.3 用 “搭积木” 理解组件化开发
我们可以把整个 Vue 应用比作 “一栋房子”,组件就是 “积木块”:
- 房子(整个应用)= 多个大积木(页面组件,如 HomeView、AboutView)+ 多个小积木(通用组件,如导航栏、按钮);
- 每个积木(组件)都有自己的形状(模板)、功能(逻辑)和颜色(样式);
- 我们可以重复用同一个积木(比如多个页面用同一个按钮组件),也能把小积木拼到大积木里(比如首页组件里放导航栏组件)。
1.4 组件化开发的 4 大优势
为什么一定要用组件?因为它能解决 “代码混乱、难维护” 的问题:
- 可复用性:写一次组件,在多个页面引用(比如 “添加按钮” 组件,在用户页、商品页都能用),不用重复写代码;
- 可维护性:组件独立封装,出问题只改这一个文件(比如导航栏样式要改,只改导航栏组件),不影响其他部分;
- 协作高效:团队分工时,甲写导航栏组件,乙写列表组件,不用互相等,开发速度快;
- 测试友好:可以单独测试一个组件(比如测试按钮点击功能),不用测整个页面。
二、组件的核心流程:创建→导入→使用
我们从实战出发,一步步实现 “创建子组件→在父组件中导入→使用子组件” 的完整流程,以 “创建 PersonDetail 子组件,在 Components 父组件中使用” 为例。
2.1 步骤 1:创建单文件组件(子组件)
首先,在src/components文件夹下新建PersonDetail.vue(子组件,负责显示个人信息),代码如下:
<template>
<div>
<h3>我是一个子组件....</h3>
</div>
</template>
<script>
// 引入defineComponent:主要作用是提供TypeScript类型推断(即使不用TS,加它也更规范)
import { defineComponent } from 'vue'
// 导出组件:让其他组件能导入
export default defineComponent({
name: 'PersonDetail' // 组件名,推荐和文件名一致(PersonDetail)
})
</script>
关键知识点:
defineComponent的作用:不用 TypeScript 也可以加,它能帮 Vue 更好地识别组件选项(比如name、props),避免语法错误;- 组件文件放哪?通用组件(按钮、卡片)放
src/components,页面级组件(首页、关于页)放src/views,这样结构更清晰。
2.2 步骤 2:在父组件中导入子组件
接下来,我们在src/components下新建Components.vue(父组件),并导入PersonDetail子组件。导入需要做 2 件事:
- 用
import语句引入子组件; - 在
components选项中声明子组件(不声明用不了,易错点!)。
父组件代码(Components.vue):
<template>
<!-- 后续在这里使用子组件 -->
<div class="parent-components"></div>
</template>
<script>
// 1. 导入子组件:@代表src目录,路径要写对(相对路径../也可以)
import PersonDetail from '@/components/PersonDetail.vue'
// 或相对路径:import PersonDetail from './PersonDetail.vue'(因为在同一文件夹)
import { defineComponent } from 'vue'
export default defineComponent({
name: 'Components', // 父组件名
// 2. 声明子组件:必须在这里写,才能在template中使用
components: {
PersonDetail // 键值对简写,等价于 PersonDetail: PersonDetail
}
})
</script>
易错点提醒:
- 路径写错:比如把
@/components/PersonDetail.vue写成@/component/PersonDetail.vue(少个 s),会报 “找不到模块”; - 没在 components 声明:导入后不写
components: { PersonDetail },直接在 template 用<PersonDetail/>,会报 “Unknown custom element”(未知自定义元素); - 命名不一致:导入时写
import Pd from './PersonDetail.vue',则components里要写Pd,template 里用<Pd/>(推荐导入名和组件名一致,避免混乱)。
2.3 步骤 3:在父组件中使用子组件
导入并声明后,就能在父组件的<template>中像用 HTML 标签一样使用子组件了,标签名就是components中声明的名称(这里是PersonDetail)。
修改父组件(Components.vue)的模板部分:
<template>
<div class="parent-components">
<h2>我是父组件(Components.vue)</h2>
<!-- 使用子组件:标签名和components中声明的一致 -->
<PersonDetail/>
<!-- 还能重复使用:多次引用同一个子组件 -->
<PersonDetail/>
</div>
</template>
<script>
// 导入和声明代码不变(同上一步)
import PersonDetail from '@/components/PersonDetail.vue'
import { defineComponent } from 'vue'
export default defineComponent({
name: 'Components',
components: { PersonDetail }
})
</script>
<style scoped>
.parent-components {
padding: 20px;
border: 1px solid #eee;
}
</style>
效果:
启动项目后,父组件会显示 “我是父组件”,下面跟着两个 “我是一个子组件....”—— 这说明我们成功在父组件中使用了子组件,还实现了组件复用!
三、核心通信:父组件给子组件传值(用 props)
组件之间经常需要 “传数据”,比如父组件有 “刘备” 的名字和年龄,要传给子组件显示。Vue 中父给子传值的核心就是props,我们分步骤讲清楚。
3.1 什么是 props?
props是子组件用来接收父组件传递数据的 “接口” —— 父组件把数据 “送” 到 props 里,子组件从 props 里 “拿” 数据来用。
特点:
- 单向传递:只能父组件传给子组件,子组件不能直接改父组件传过来的 props(改了会报错,易错点!);
- 命名一致:父组件传的 “参数名” 和子组件 props 接收的 “参数名” 必须完全一致(比如父传
names,子不能写name)。
3.2 父给子传值的 4 个步骤(实战)
还是用 “Components 父组件” 给 “PersonDetail 子组件” 传值为例,传递 3 个数据:message(信息标题)、names(姓名)、age(年龄)。
步骤 1:父组件准备数据并传递
父组件(Components.vue)中,在使用子组件的标签上,通过 “属性” 传递数据。分两种情况:
- 静态传值:传递固定值(比如字符串 “个人信息”),直接写
属性名="值"; - 动态传值:传递变量(比如父组件的
userName变量),要加v-bind:或简写:(比如:names="userName")。
这里先演示静态传值,父组件代码:
<template>
<div class="parent-components">
<h2>我是父组件(Components.vue)</h2>
<!-- 父组件给子组件传值:属性名=值 -->
<PersonDetail
message="个人信息" <!-- 静态传值:固定字符串 -->
names="刘备" <!-- 静态传值:姓名 -->
age="50" <!-- 静态传值:年龄(注意:这里是字符串"50") -->
/>
</div>
</template>
<script>
// 导入和声明子组件(不变)
import PersonDetail from '@/components/PersonDetail.vue'
import { defineComponent } from 'vue'
export default defineComponent({
name: 'Components',
components: { PersonDetail }
})
</script>
步骤 2:子组件用 props 接收数据
子组件(PersonDetail.vue)中,在export default里添加props选项,声明要接收的参数(数组形式或对象形式,数组形式简单,对象形式更规范)。
数组形式(简单,适合初学者):
<template>
<div>
<h3>我是一个子组件....</h3>
<!-- 步骤3:子组件使用接收的props数据(直接用参数名) -->
<p>信息标题:{{ message }}</p>
<p>姓名:{{ names }}</p>
<p>年龄:{{ age }}</p>
</div>
</template>
<script>
import { defineComponent } from 'vue'
export default defineComponent({
name: 'PersonDetail',
// 步骤2:用props数组接收父组件传的参数(参数名必须和父组件一致)
props: ['message', 'names', 'age']
})
</script>
步骤 3:子组件使用 props 数据
子组件接收后,直接在<template>中用{{ 参数名 }}显示数据(不用像data里的变量那样return,直接用!),就是上面代码中<p>标签里的内容。
步骤 4:配置路由,测试效果
我们需要配置路由,让父组件能通过 URL 访问,验证传值是否成功:
- 配置
src/router/index.js(添加 Components 路由):
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
// 导入Components父组件
import Components from '@/components/Components.vue'
const routes = [
{ path: '/', name: 'home', component: HomeView },
{ path: '/about', name: 'about', component: () => import('../views/AboutView.vue') },
// 新增Components路由
{ path: '/components', name: 'components', component: Components }
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
- 在
App.vue中添加路由链接(让我们能点击访问):
<template>
<div id="app">
<nav>
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link> |
<router-link to="/components">Components(父组件)</router-link> <!-- 新增 -->
</nav>
<router-view/>
</div>
</template>
- 测试:启动项目(
npm run serve),点击 “Components(父组件)”,页面会显示:
我是父组件(Components.vue)
我是一个子组件....
信息标题:个人信息
姓名:刘备
年龄:50
—— 这说明父组件的数值成功传给子组件了!
3.3 进阶:props 的对象形式(更规范)
数组形式的 props 只能声明 “要接收哪些参数”,但不能限制参数类型(比如父传age="abc",子组件也能接收,导致显示错误)。推荐用对象形式的 props,指定参数类型、默认值、必填项,更严谨。
修改子组件(PersonDetail.vue)的 props 为对象形式:
<script>
import { defineComponent } from 'vue'
export default defineComponent({
name: 'PersonDetail',
// props对象形式:指定类型、默认值、必填
props: {
// message:字符串类型,必填
message: {
type: String, // 类型:String(还支持Number、Boolean、Array等)
required: true, // 必填:父组件必须传这个参数,不传会报警告
default: '默认信息' // 默认值:如果父组件没传,就用这个(required为true时,default无效)
},
// names:字符串类型,必填
names: {
type: String,
required: true
},
// age:数字类型,可选,默认值18
age: {
type: Number, // 类型:Number(父传的时候要传数字,不是字符串!)
required: false,
default: 18 // 默认值:父没传的话显示18
}
}
})
</script>
关键提醒:传值类型要匹配
如果子组件 props 指定age是Number类型,父组件传值时要注意:
- 错误:
age="50"(传的是字符串 “50”,和 Number 类型不匹配,会报警告); - 正确:
:age="50"(加:动态传值,传的是数字 50,类型匹配)。
修改父组件的传值方式:
<PersonDetail
message="个人信息"
names="刘备"
:age="50" <!-- 动态传值,确保是Number类型 -->
/>
3.4 props 的重要规则:不能直接修改
子组件绝对不能直接修改 props 的值!比如子组件里写this.age = 51,会报错,因为 props 是父组件传的,要改只能让父组件改,子组件通过 “事件” 通知父组件(后面会讲子给父传值)。
错误示例(禁止!):
<script>
import { defineComponent } from 'vue'
export default defineComponent({
name: 'PersonDetail',
props: ['age'],
methods: {
changeAge() {
this.age = 51 // 错误!不能直接改props
}
}
})
</script>
四、常见易错点总结(避坑指南)
我们把前面提到的易错点汇总,帮大家避免踩坑:
-
template 没有根元素(Vue 2)Vue 2 中
<template>必须有一个根元素(比如外层包<div>),否则报错;Vue 3 支持多根,但推荐单根,保持兼容。 -
组件没在 components 选项中声明导入组件后,一定要在
components: { 组件名 }中声明,否则用的时候会报 “Unknown custom element”。 -
props 命名不一致父组件传的参数名(如
names)和子组件 props 接收的参数名(如name)必须完全一致,大小写敏感(names≠name)。 -
动态传值漏加
:传动态值(变量、数字、布尔值)时,必须加:或v-bind:,否则传的是字符串,导致类型不匹配。 -
子组件直接修改 propsprops 是父组件传的,子组件只能用不能改,要改必须通过事件通知父组件修改(后续讲子给父传值)。
-
组件路径写错导入组件时,路径要写对(
@代表 src 目录),比如@/components/PersonDetail.vue,少个s或错个字母都会找不到模块。
五、总结:组件是 Vue 的核心
学到这里,我们已经掌握了 Vue 组件的核心:
- 组件是
.vue文件,包含<template>(显示)、<script>(逻辑)、<style scoped>(样式); - 组件使用流程:创建子组件→父组件导入→父组件声明→父组件使用;
- 父给子传值用
props,要注意类型匹配,不能直接修改 props。
组件是 Vue 组件化开发的基础,掌握这些,我们就能开始拆分页面(比如把首页拆成导航栏、轮播图、列表组件),后续还会学习 “子给父传值”“兄弟组件传值”“动态组件” 等进阶内容,一步步构建复杂应用!
更多推荐



所有评论(0)