对于组件化编程,组件之间的通信技术无疑是非常重要的内容,需要将细节牢牢把握。
组件通信,就是子组件放置在父组件内之后,父组件如何向子组件传递参数以及子组件如何与外部组件进行互动。
这部分的知识很重要,需要展开一一掌握,分下面的三种情况进行逐一说明。当然,考虑到组件通信的复杂,后面我写一篇很简单的组件通信,不必考虑父子组件的区别。
1、父组件向子组件的多种传值方式
演示代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue.js的组件化编程:组件通信</title>
<script src="./js/vue2.js"></script>
</head>
<body>
<h1>父组件向子组件传值</h1>
<div id="app">
<my-componect my-title="组件标题1" my-content="组件内容1"></my-componect>
<my-componect v-bind:my-title="'组件标题2'" :my-content="'组件内容2'"></my-componect>
<my-componect v-bind:my-title="item.title" :my-content="item.content"></my-componect>
</div>
<script type="text/javascript">
Vue.component("myComponect",{
props:['myTitle','myContent'],
template:`<div>
<h3>{{myTitle}}</h3>
<h6>{{myContent}}</h6>
</div>`
});
var vm=new Vue({
el:"#app",
data:{
item:{
title:'组件标题',
content:'组件内容'
}
}
});
</script>
</body>
</html>
显示结果:
父组件可以有3种方法向子组件传递数据,子组件的props接收的是数组,可以接收多个参数。
⑴ 直接传字符串,是静态文本;
⑵ 传JavaScript表达式;
⑶ 传对象属性值;
⑷ 传对象。
注意点:
⑴ props与父组件的data命名最好不要相同。
⑵ v-bind: title可以简写为:title。
传JavaScript表达式或者对象,可以看下面的例子。
网页代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue.js的组件化编程:组件通信</title>
<script src="./js/vue2.js"></script>
</head>
<body>
<h1>父组件向子组件传值</h1>
<div id="app">
<my-componect :my-data=" { title:'这是组件标题',content:'这是组件内容'}"></my-componect>
<my-componect :my-data="item"></my-componect>
<my-componect :my-data="item"></my-componect>
</div>
<script type="text/javascript">
Vue.component("myComponect",{
props:['myData'],
template:`<div>
<h3>{{myData.title}}</h3>
<input type="text" v-model="myData.content" />
</div>`
});
var vm=new Vue({
el:"#app",
data:{
item:{
title:'组件标题',
content:'组件内容'
}
}
});
</script>
</body>
</html>
显示结果:
可以看出,如果是以对象的方式进行传值,那么当对象的属性改变时,子组件的内容也会发生改变,其实,这也可以作为子组件向外传值的一种方式就是引用方式传值。
2、子组件向父组件传值以及子组件调用父组件的方法(函数)
结合上面父组件向子组件传递对象,练习下面的代码:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vue.js的组件化编程:组件通信</title>
<script src="./js/vue2.js"></script>
</head>
<body>
<div id="app">
<my-score-sheet v-for="cla in classList" :my-class="cla" :key="cla.id" @data-change="averageFunc"></my-score-sheet>
<p align="center"><b>学校数学总平均分:{{averageMaths}},学校语文总平均分:{{averageChinese}}</b></p>
</div>
<script type="text/javascript">
Vue.component("myScoreSheet",{
props:['myClass'],
data(){
return {
classSign:{
id:0,
name:'',
students:[],
},
averageMaths:0.0,
averageChinese:0.0
}
},
created() {
this.classSign = this.myClass;
},
template:`<div>
<h1 align="center">{{classSign.name}}</h1>
<table align="center">
<tr align="center">
<td>学号</td>
<td>姓名</td>
<td>语文</td>
<td>数学</td>
</tr>
<tr v-for="student in classSign.students" :key="student.id">
<td>{{student.id}}</td>
<td>{{student.name}}</td>
<td ><input type="number" v-model="student.maths" @input="dataChange()"/></td>
<td ><input type="number" v-model="student.chinese" @input="dataChange()"/></td>
</tr>
</table>
<p align="center">班级数学平均分:{{averageMaths}},班级语文平均分:{{averageChinese}}</p>
</div>`,
methods:{
dataChange(){
let sumMaths=0.0;
let sumChinese=0.0
this.classSign.students.forEach(element=>{
sumMaths+=parseFloat(element.maths);
sumChinese+=parseFloat(element.chinese);
});
this.averageMaths=sumMaths/Object.keys(this.classSign.students).length;
this.averageChinese=sumChinese/Object.keys(this.classSign.students).length;
this.averageMaths=Math.round(this.averageMaths*100)/100;//保留两位小数
this.averageChinese=Math.round(this.averageChinese*100)/100;//保留两位小数
// this.$emit('data-change',this.classSign);
this.$emit('data-change');//调用父组件的方法(函数)
}
}
});
var vm=new Vue({
el:"#app",
data:{
classList:[
{ id:1,name:'学霸班',students:[{id:1001,name:'小明',maths:97,chinese:89},{id:1002,name:'小华',maths:83,chinese:91},{id:1003,name:'小黄',maths:75,chinese:86}] },
{ id:2,name:'青云班',students:[{id:2001,name:'小宋',maths:87,chinese:82},{id:2002,name:'小丽',maths:81,chinese:84},{id:2003,name:'小云',maths:82,chinese:89}] }
],
averageMaths:0.0,
averageChinese:0.0
},
methods:{
averageFunc(){
let studentCount=0;
let sumMaths=0.0;
let sumChinese=0.0
this.classList.forEach(element => {
element.students.forEach(student => {
sumMaths+=parseFloat(student.maths);
sumChinese+=parseFloat(student.chinese);
studentCount+=1;
});
});
this.averageMaths=sumMaths/studentCount;
this.averageChinese=sumChinese/studentCount;
this.averageMaths=this.averageMaths.toFixed(2);
this.averageChinese=this.averageChinese.toFixed(2);
}
}
});
</script>
</body>
</html>
输出结果:
说明:
⑴ 子组件调用父组件通过this.$emit('data-change')来完成,当然也可以传递参数给父组件,比如:
this.$emit('data-change',this.classSign)
那么父组件的函数就应该定义为:
dataChange(cla){}
其中的cla就表示this.classSign。
⑵ 子组件在准备了props和data后,在created中进行对象的绑定:
this.classSign = this.myClass;
因为是引用传值,那么在子组件中的数据改变也同步到了父组件的classList中。
⑶ 子组件的数据定义,classSign内部的学生数据定义为students:[],在进行数据绑定的时候就接收了父组件传来的学生列表。
⑷ 保留两位小数的函数:
Math.round(this.averageMaths*100)/100
或者
this.averageMaths.toFixed(2)
⑸ 数组的统计长度:Object.keys(this.classSign.students).length,this.classSign.students是数组名称。
3、平行组件之间的通讯,通过emit结合$refs进行。
具体的内容在《Vue组件化编程的基础知识要点》中有详细记录。
通过上面的总结,可能会觉得Vue.js中组件化编程比较麻烦,这么多内容?!其实如果深入思考,完全不是这么一回事情,可以简单明了地抛开上面的技术细节。
说到底就是自定义事件和事件队列,这个思想与传统的C/S下的组件化编程几乎是相同的,想明白了这一点,组件通信就比较容易了。
后面会进行总结。