组件简介


-
模块
理解:向外提供特定功能的 js 程序,一般就是一个 js 文件
为什么:js 文件很多很复杂
作用:复用 js,简化 js 的编写,提高 js 运行效率
-
组件
定义:用来实现局部功能的代码和资源的集合(html/css/js/image...)
为什么:一个界面的功能很复杂
作用:复用编码,简化项目编码,提高运行效率
-
模块化
当应用中的 js 都以模块来编写的,那这个应用就是一个模块化的应用
-
组件化
当应用中的功能都是多组件的方式来编写的,那这个应用就是一个组件化的应用
组件的使用
1、基础概念
Vue 中的 Component (组件) 是可复用、封装的 Vue 实例,是构建 Vue 应用的基本单元 ------ 它将页面 UI 拆分为独立、可复用的模块,每个组件包含自身的模板(HTML)、逻辑(JS)和样式(CSS),实现代码的模块化与复用。
2、核心特点
- 可复用性:一个组件可在应用的多个位置重复使用(如按钮组件、卡片组件),减少冗余代码;
- 封装性:组件内部的 HTML 结构、数据逻辑、样式是封闭的,对外仅暴露必要的接口(如 props 传参、emit 事件);
- 独立性:拥有自己的作用域(数据、方法仅在组件内生效),不同组件间互不干扰;
- 组合性:组件可嵌套使用(父组件包含子组件),形成复杂的页面结构。
3、注册方式1
javascript
<script src="./js/vue.js"></script>
<script>
//1. 创建组件构造器 (预设选项)
const com = Vue.extend({
template: "<h1>我是组件。</h1>"
});
//2.注册组件
Vue.component(
"com1",
com
);
//3.创建 vue 实例后,组件就可以使用了。
const vue = new Vue({
el: "#box"
});
</script>
4.4、注册方式2
html
<div id="app">
<hello></hello>
</div>
<script src="./js/vue.js"></script>
<script>
Vue.component(
'hello',
{template:'<h1>同学,你好(2)</h1>'}
);
const vm = new Vue(
{
el:"#app"
}
);
</script>
4.5、组件携带数据的情况
javascript
<div id="app">
<!-- 使用定义的组件 -->
<temp3></temp3>
<my-com></my-com>
</div>
<!-- 定义组件模板 -->
<script type="text/x-template" id="com3">
<div><h1>你好, {{name}} 同学</h1></div>
</script>
<script type="text/x-template" id="MyCom">
<div><h1>你好, [MyCom]</h1></div>
</script>
<script src="../js/vue.min.js"></script>
<script>
// 注册组件
Vue.component(
'temp3', {
template:'#com3',
data(){
return { name:'ANDY' }
}
});
Vue.component(
'myCom', {
template:'#MyCom'
});
// 创建根实例
const vm = new Vue({
el:"#app"
});
</script>
总结
- Vue中使用组件的三大步骤:
定义组件(创建组件)
注册组件
使用组件(写组件标签)
- 如何定义一个组件?
使用Vue.extend(options)创建,其中options和new Vue(options)时传入的options几乎一样,但也有点区别:
el不要写,为什么?
最终所有的组件都要经过一个vm的管理,由vm中的el决定服务哪个容器
data必须写成函数,为什么?
避免组件被复用时,数据存在引用关系
- 如何注册组件?
局部注册:new Vue的时候传入components选项
全局注册:Vue.component('组件名',组件)
编写组件标签:
私有组件
1、基础概念
在 Vue2 中,私有组件指仅能在某个特定组件(或 Vue 实例)内部使用的组件,其作用域被限定在注册它的父组件内,无法在其他组件 / 实例中直接调用,是组件模块化和作用域隔离的重要实现方式。
2、定义方式
私有组件通过组件内的 components 选项注册,步骤如下:
- 先定义子组件(如对象形式或单独文件);
- 在父组件的
components中声明该子组件(键为组件名,值为组件对象 / 引入的组件); - 在父组件模板中直接使用子组件标签。
3、核心特点
- 作用域局限:仅注册它的父组件及其子组件(若有)可使用,外部组件无法访问;
- 避免命名冲突:不同父组件可注册同名私有组件,互不影响;
- 按需注册:无需全局挂载,减少全局作用域污染,优化性能(仅父组件加载时初始化)。
4、使用场景
适用于仅在特定父组件内使用、无需全局复用的子组件,例如:
- 某个页面独有的功能模块(如详情页的 "评论项" 组件);
- 父组件内部的辅助 UI 组件(如自定义按钮、表单项)。
5、示例代码
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
.pr {
color: orange;
}
</style>
</head>
<body>
<div id="app">
<login></login>
<register></register>
</div>
</body>
<script src="./js/vue.js"></script>
<!-- 定义组件模板 -->
<script id="regTemp" type="text/x-template">
<div><h1>这是组件{{2}}</h1></div>
<h1 class="pr">这是组件{{1}}</h1>
</script>
<script>
new Vue({
el: "#app",
components: {
login: {
template: `<h1 class="pr">这是组件{{1}}</h1>`
},
register: {
template: '#regTemp'
}
}
});
</script>
</html>
5.6、与全局组件的区别

组件中的data
1、基础概念
在 Vue 组件中,data 是组件内部响应式状态的核心容器,用于存储组件自身的动态数据,是模板渲染、逻辑交互的基础,也是 Vue 数据驱动视图 核心思想的直接体现。
2、核心作用
1. 存储组件私有动态状态
data 专门存放组件自身拥有、可修改的私有数据(区别于 props 接收的外部只读数据),涵盖:
- 表单输入值(如输入框的
value); - 页面展示的动态数据(如列表数据、计数
count); - 控制 UI 状态的标识(如弹窗显隐
showModal、加载状态loading)。
2. 支撑模板的响应式渲染
data 中的数据与模板(`)绑定后,数据的任何修改都会自动触发视图更新(Vue 的响应式系统),无需手动操作 DOM。
2、测试代码
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style>
.pr {
color: orange;
}
</style>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
<temp1></temp1>
</div>
</body>
<script>
Vue.component(
'temp1',
{
template: '<h1>我是组件, data: {{info}}</h1>',
data: function () {
return { info: '这是数据' }
}
}
);
new Vue({ el: "#app" });
</script>
</html>
组件的切换
1、功能描述
Vue 中组件切换指根据业务条件(如状态、路由、用户操作)在页面中替换显示不同组件,核心有 条件渲染、动态组件 两类核心方式,另有路由切换适配页面级组件场景。
2、条件渲染
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="app2">
<div @click="swap">切换组件</div>
<login v-if="flag"></login>
<register v-else="flag"></register>
</div>
</body>
<script>
Vue.component(
'login',
{
template:'<h2>登录组件。</h2>'
}
);
Vue.component(
'register',
{
template:'<h2>注册组件</h2>'
}
);
new Vue({
el:"#app2",
data:{ flag:false },
methods:{
swap(){
this.flag = !this.flag;
}
}
});
</script>
</html>
3、动态组件
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="app2">
<a href="#" @click.prevent="comName='login'">显示登录</a>
<a href="#" @click.prevent="comName='register'">显示注册</a>
<component :is="comName"></component>
</div>
</body>
<script>
Vue.component(
'login',
{
template: '<h2>登录组件。</h2>'
}
);
Vue.component(
'register',
{
template: '<h2>注册组件。</h2>'
}
);
new Vue({
el: "#app2",
data: { comName: 'login' },
});
</script>
</html>
组件间传值
1、功能描述
Vue 组件间传值 (组件通信) 是实现组件协作的核心,核心遵循【单向数据流】原则
需根据组件层级关系(父子、子父、兄弟、跨级 / 任意组件)选择对应方案.
2、父传子数据
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
<child message="hello!"></child>
</div>
</body>
<script>
// 注册
Vue.component('child', {
// 声明 props
props: ['message'],
// 同样也可以在 vm 实例中像 "this.message" 这样使用
template: '<span>{{ message }}</span>'
})
// 创建根实例
new Vue({
el: '#app'
})
</script>
</html>
3、动态传递
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
<div>
<input v-model="parentMsg">
<br>
<child v-bind:message="parentMsg"></child>
</div>
</div>
</body>
<script>
// 注册
Vue.component('child', {
// 声明 props
props: ['message'],
// 同样也可以在 vm 实例中像 "this.message" 这样使用
template: '<span>{{ message }}</span>'
})
// 创建根实例
new Vue({
el: '#app',
data: {
parentMsg: '父组件内容'
}
})
</script>
</html>
4、子传父数据
Vue.js 组件 - 自定义事件
父组件是使用 props 传递数据给子组件,但如果子组件要把数据传递回去,就需要使用自定义事件!
我们可以使用 v-on 绑定自定义事件, 每个 Vue 实例都实现了事件接口(Events interface),即:
- 使用
$on(eventName)监听事件 - 使用
$emit(eventName)触发事件
另外,父组件可以在使用子组件的地方直接用 v-on 来监听子组件触发的事件。
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="./js/vue.js"></script>
</head>
<body>
<div id="app">
<div id="counter-event-example">
<p>{{ total }}</p>
<button-counter v-on:increment="incrementTotal"></button-counter>
<button-counter v-on:increment="incrementTotal"></button-counter>
</div>
</div>
</body>
<script>
// 定义一个计数器组件
Vue.component('button-counter', {
template: '<button v-on:click="incrementHandler">{{ counter }}</button>',
data: function () {
return {
counter: 0
}
},
methods: {
incrementHandler: function () {
this.counter += 1
// $emit(eventName) 触发事件
// 子组件已经和它外部完全解耦了。它所做的只是触发一个父组件关心的内部事件。
// 使用 increment 指向父级的事件
this.$emit('increment')
}
},
})
new Vue({
el: '#counter-event-example',
data: {
total: 0
},
methods: {
incrementTotal: function () {
this.total += 1
}
}
})
</script>
</html>
data 必须是一个函数
每个实例可以维护一份被返回对象的独立的拷贝,如果 data 是一个对象则会影响到其他实例
5、方式对比

章节案例
1、案例描述
添加水果案例

2、代码示例
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="./js/vue.js"></script>
<style>
body {
background: #F5F5F5;
}
ul {
list-style: none;
padding: 0px;
}
span {
margin-left: 10px;
}
[type='text'] {
width: 120px;
height: 30px;
}
[type='button'] {
width: 80px;
height: 30px;
}
</style>
</head>
<body>
<div id="app">
<ipt-box @func1="addItem"></ipt-box>
<ul>
<li v-for="item in list" :key="item.id">
<span>水果名称: {{item.fruitName}}</span>
<span>价格: {{item.price}}</span>
</li>
</ul>
</div>
<!-- 定义组件 -->
<template id="temp1">
<div>
<div>
<label>水果名称</label>
<input type="text" v-model="fruitName" />
</div>
<div>
<label>水果价格</label>
<input type="text" v-model="price" />
</div>
<div>
<input type="button" value="添加数据" @click="confirm" />
</div>
</div>
</template>
</body>
<script>
var iptBox = {
template: '#temp1',
data() {
return { list: [] };
},
methods: {
confirm: function () {
var f = {
id: this.id,
fruitName: this.fruitName,
price: this.price
};
this.$emit('func1', f);
}
}
};
var vm = new Vue(
{
el: '#app',
data: {
list: [
{ id: 'f1', fruitName: '苹果', price: 5.6 },
{ id: 'f2', fruitName: '雪梨', price: 4.3 },
{ id: 'f3', fruitName: '哈密瓜', price: 2.3 },
{ id: 'f4', fruitName: '葡萄', price: 4.4 }
]
},
methods: {
addItem: function (fruit) {
this.list.push(fruit);
}
},
// 注册局部组件
components: {
'ipt-box': iptBox
}
}
);
</script>
</html>