组件是带有名称的可复用实例,将可重用的部分进行组件化,从而方便项目的开发和维护
组件多次调用,每个组件不会互相影响
创建组件
在components文件夹内进行创建组件
全局注册组件
在main.js
内进行组件注册,且必须创建在 new Vue上面
例子:演示创建全局组件
// 目标: 全局注册 (一处定义到处使用)
// 1. 创建组件 - 文件名.vue
// 2. 引入组件
import Pannel from './components/Pannel'
// 3. 全局 - 注册组件
/*
语法:
Vue.component("组件名", 组件对象)
*/
Vue.component("PannelG", Pannel)
全局组件创建后,就可以在引用后当作标签在任意Vue文件中的template标签内部使用
标签会被当做组件进行解析,将组件里封装的标签替换到当前的位置
局部注册组件
在需要使用组件的Vue文件内引入、注册、使用
import 组件对象 from 'vue文件路径'
export default {
components: {
"组件名": 组件对象
}
}
例子
<template>
<div>
<!-- 组件的使用 -->
<TodoHeader></TodoHeader>
<TodoMain></TodoMain>
<TodoFooter></TodoFooter>
</div>
</template>
<script>
// 1. 子组件引入
import TodoHeader from "./components/TodoHeader.vue";
import TodoMain from "./components/TodoMain.vue";
import TodoFooter from "./components/TodoFooter.vue";
//2. 组件注册
export default {
components: {
TodoHeader,
TodoMain,
TodoFooter,
},
}
</script>
组件使用总结:
- (创建)封装html+css+vue到独立的.vue文件中
- (引入注册)组件文件 => 得到组件配置对象
- (使用)当前页面当做标签使用
scoped 作用
解决多个组件样式名冲突的问题
//设置在style标签里
<style scoped>
</style>
在style上加入scoped属性, 就会在此组件的标签上加上一个随机生成的data-v开头的属性
而且必须是当前组件的元素, 才会有这个自定义属性, 才会被这个样式作用到
且只在当前的Vue组件内部生效
Vue 组件通信
由于每个组件之间相互都是独立的,这就使组件之间进行信息的传递造成了许多的困难, 以使用组件的Vue文件传递给组件文件的子组件 或是子组件项父级组件进行信息的传递
$emit(eventnam)
: 触发当前实例上的事件,附加参数都会传给监听器回调。$on(eventname)
:监听当前实例上的自定义事件。事件可以由 vm.$emit 触发。回调函数会接收所有传入事件触发函数的额外参数。
props 向子组件传递数据
props一般是自定义的数组格式,向子组件传递的数据保存在数组里,用""
进行包裹
传递过来的数据类似于data
里的数据,在除<template>
标签外的地方使用需要加上this.
且props内部属性为只读属性,类似于const
的定义,地址无法改变,但内部的值可以进行修改
父级组件内的数组传入子组件中:
<template>
<ul>
<!-- 循环父文件传进来的数组 arr -->
<li v-for="obj in arr" :key="obj.id">
<div>
<!-- 通过 v-model双向绑定来控制isDone是否完成 -->
<input type="checkbox" v-model="obj.isDone" />
<!-- 将名字显示到页面 -->
<label>{{ obj.name }}</label>
<!-- 注册一个点击事件来获取点击的数组对象元素id -->
<button @click="delFn(obj.id)"></button>
</div>
</li>
</ul>
</template>
<script>
export default {
// 定义props 通过自定义属性来调入数组list
props: ["arr"],
};
</script>
父级组件内部的传入方式
<template>
<div>
<!--定义一个自定义属性arr 来存储数组list -->
<TodoMain :arr="list" @del="deleteFn"></TodoMain>
</div>
</template>
<script>
// 子组件引入
import TodoMain from "./components/TodoMain.vue";
export default {
components: {
TodoMain,
},
data() {
return {
list: [
{ id: 100, name: "吃饭", isDone: true },
{ id: 201, name: "睡觉", isDone: false },
{ id: 103, name: "打豆豆", isDone: true },
],
};
},
</script>
<style>
</style>
监听子组件的事件
由于单项数据流的原因,子组件修改父组件的数据需要通知父级组件进行修改,如果仅在子组件内进行修改会导致子组件和父级组件内的数据出现偏差,从而导致数据修改失败
父级组件通过Vue提供的自定义事件@来监听子组件内的事件发生, 子组件通过事件方法内地
$emit
方法来绑定父级组件的自定义事件从而达到向父级组件提交数据修改并更新父级组件内数据
父级组件创建自定义事件
在子组件的标签上添加自定义事件 和对应的数据修改方法
<!-- 在子组件标签上添加自定义事件 事件后面的方法写在父级组件内 -->
<Headler @creat="addData"></Headler>
export default{
methods:{
//方法接收子组件传过来的参数
addData(data){
this.list.push(data)
}
}
}
子组件触发事件并传回修改数据
通过事件里的$emit()
函数来绑定父级组件内的自定义事件并讲数据传回父级组件
语法
this.$emit('父级组件内绑定的自定义事件名',要传回的参数1,要传回的参数2...)
例子
<input v-model="task" @keydown.enter="downFn" placeholder="输入任务名称-回车确认" autofocus/>
export default {
data() {
return {
task: "",
};
},
methods: {
downFn() {
// 子组件将收集到的数据传给父组件进行操作
// 通过$emit函数将子组件里的值传给父组件
this.$emit("create", this.task);
//添加后清除文本框
this.task = "";
},
},
}
EventBus 同级组件跨组件通信
两个组件的关系非常的复杂,通过父子组件通讯是非常麻烦的。这时候可以使用通用的组件通讯方案:事件总线(event-bus)
创建一个**EventBus**
文件夹,在里面添加一个**index.js**
文件
在内部创建一个空的Vue对象
import Vue from 'vue'
// 导出空白vue对象
export default new Vue()
在子组件上导入EventBus
<script>
import eventBus from '../EventBus'
</script>
子组件1通过$emit()
发送数据
EventBus.$emit('send',发送数据1....)
<template>
<section>
<h1>left</h1>
<el-button type="primary" @click="isClick">点击</el-button>
</section>
</template>
<script>
export default {
data(){
return{
message:'这是一个要发送的数据'
}
}
methods: {
isClick() {
EventBus.$emit('send', message);
}
},
}
</script>
子组件2通过EventBus.$on()
来接收EventBus.$emit()
传输过来的数据
$on()和$emit()需要绑定同一自定义事件名才能进行数据传输
EventBus.$on('send',(接收数据1,...)=>处理数据的方法)
<template>
</template>
<script>
export default {
data(){
return{
message:''
}
},
create(){
//在on中定义一个方法来处理传过来的数据
EventBus.$on('send',(messages)=>this.message=messages)
}
}
</script>