1、父子组件间的通信
- 子组件不应该直接向服务器请求数据
- 顶层组件请求数据之后,再向子组件传递数据(通信)
- 父向子:通过props(属性)父组件向子组件传递数据
- 子向父:通过事件子组件向父组件发送消息
2、props基本用法
- 1、在顶层vue实例中(父组件)创建需要用于传递的数据,并注册一个子组件cpn
var app=new Vue({
el:"#app",
data:{
message:"关于父组件传值",
movies:['一秒钟','我和我的家乡','夺冠','雪国列车']
},
components:{
'cpn':sonCpn//这里也可以直接按es6的写法,写为cpn
}
}
);
- 2、创建子组件,并使用分离方式在html中写组件模板
const sonCpn={
template:'#cpn',
//子组件中定义props属性接受父组件传来的值
props:['cmoives','cmessage'],
//最好写下data,其实不写也可以
data(){
return {}
}
}
组件模板,注意是通过id=‘cpn‘和子组件构成联系的
<template id="cpn">
<!--在子组件是使用父组件传递过来的信息-->
<div>
<div>{{cmessage}}</div>
<ul>
<li v-for="item in cmoives">{{item}}</li>
</ul>
</div>
</template>
- 3、在html中使用自定义父组件
<div id="app">
<!--这里自定义了两个属性cmovices和cmessage向子组件传值(movies数组和message变量),实际就是使用props属性数据-->
<cpn :cmoives='movies' :cmessage='message'></cpn>
</div>
完整代码:
<body>
<div id="app">
<!--这里自定义了两个属性cmovices和cmessage向子组件传值(movies数组和message变量)-->
<cpn :cmoives='movies' :cmessage='message'></cpn>
</div>
<template id="cpn">
<!--在子组件是使用父组件传递过来的信息-->
<div>
<div>{{cmessage}}</div>
<ul>
<li v-for="item in cmoives">{{item}}</li>
</ul>
</div>
</template>
<script type="text/javascript">
const sonCpn={
template:'#cpn',
//子组件中定义props属性接受父组件传来的值
props:['cmoives','cmessage'],
//最好写下data,其实不写也可以
data(){
return {}
}
}
var app=new Vue({
el:"#app",
data:{
message:"关于父组件传值",
movies:['一秒钟','我和我的家乡','夺冠','雪国列车']
},
components:{
'cpn':sonCpn//这里也可以直接按est的写法,写为cpn
}
}
);
</script>
</body>
3、props值的两种方式
props值有两组方式,
- 方式1:字符串数组,数组中的元素就是传递的值就是传递时的名称
//props字符串数组
props:['cmoives','cmessage'],
--------
//子组件中字符串数组元素对应属性值
<cpn :cmoives='movies' :cmessage='message'></cpn>
- 方式2:对象,对象可以设置传递时的类型,也可以设置默认值
当需要对props进行类型等验证时,就需要对象写法了
支持验证的类型包含:
string
number
boolean
array
object
date
function
symbol
格式:
使用对象
props:{
//使用对象时,对象名对应模板的属性,同时可以指定接受数据的类型
cmoives:Array,
cmessage:String
},
提供默认值
<script type="text/javascript">
const sonCpn={
template:'#cpn',
//子组件中定义props属性接受父组件传来的值
props:{
//使用对象时,对象名对应模板的属性,同时可以指定接受数据的类型
cmoives:Array,
//提供默认值
cmessage:{
type:String,
default:'我是默认值',
require:true//必传属性
}
},
//最好写下data,其实不写也可以
data(){
return {}
}
}
var app=new Vue({
el:"#app",
data:{
//message:"关于父组件传值",屏蔽掉message后,由默认值来显示
movies:['一秒钟','我和我的家乡','夺冠','雪国列车']
},
components:{
'cpn':sonCpn//这里也可以直接按est的写法,写为cpn
}
}
);
</script>
4、props驼峰标识写法
props不要写驼峰对象名,应当如下
props:{
cmoives:Array,
//非驼峰写法
cmessage:{
type:String,
default:'我是默认值'
}
},
5、子组件向父组件传值
- 通常是在子组件上发生了事件,在发生事件的同时,向父组件返回信息
- 使用$emit()来传值,第一个参数是自定义事件名,第二个参数是发射出去的信息,传给父组件的监听函数
具体的执行过程
- 1、创建子组件
//创建子组件
const sonCpn={
//关联字符串模板
template:'#cpn',
//子组件的data必须是函数function而不能是对象
data(){
return {
categories:[
{id:'aa',name:'热门推荐'},
{id:'bb',name:'手机数码'},
{id:'cc',name:'家用电器'},
{id:'dd',name:'电脑显卡'},
]
}
},
- 2、子组件关联template模板字符串
<!--子组件模板-->
<template id="cpn">
<div>
<button v-for="item in categories" @click="btnClick(item)">{{item.name}}</button><br/>
</div>
</template>
- 3、创建父组件(这里使用顶级root的vue实例作为父组件)
var app=new Vue({
el:"#app",
data:{
},
methods:{
//创建父组件的监听函数,item是子组件$emit()传递过来的值
cpnClick(item){
console.log(item.name);
}
},
components:{
'cpn':sonCpn//这里也可以直接按es6的写法,写为cpn
}
}
);
- 4、在html根中使用父组件
<!--父组件模板-->
<div id="app">
<!--父组件监听子组件传来的值,itemClick是自定义事件名-->
<cpn @item-click="cpnClick"></cpn>
</div>
- 5、子组件设定自定义按钮点击事件函数,将子组件数据装载到函数参数中进行传递
<template id="cpn">
<div>
<button v-for="item in categories" @click="btnClick(item)">{{item.name}}</button><br/>
</div>
</template>
- 6、子组件添加按钮事件响应函数的执行逻辑,通过$emit()函数将子组件的值发射出去
methods:{
//创建子组件向父组件传值的事件响应函数
btnClick(item){
//向父组件传值,定义一个子组件发出的自定义事件(itemClick)
//第一个参数是自定义事件名
//第二个参数是发射出去的信息,传给父组件的监听函数
this.$emit('item-click',item)
}
}
- 7、父组件监听子组件来之值
<!--父组件模板-->
<div id="app">
<!--父组件监听子组件传来的值,itemClick是$emit()里面自定义的事件名-->
<cpn @item-click="cpnClick"></cpn>
</div>
- 8、创建父组件中监听来值的处理函数
methods:{
//创建父组件的监听函数,item是子组件$emit()传递过来的值
cpnClick(item){
console.log(item.name);
}
},
完整的代码:
<body>
<!--父组件模板-->
<div id="app">
<!--父组件监听子组件传来的值,itemClick是$emit()里面自定义的事件名-->
<cpn @item-click="cpnClick"></cpn>
</div>
<!--子组件模板-->
<template id="cpn">
<div>
<button v-for="item in categories" @click="btnClick(item)">{{item.name}}</button><br/>
</div>
</template>
<script type="text/javascript">
//创建子组件
const sonCpn={
//关联字符串模板
template:'#cpn',
//子组件的data必须是函数function而不能是对象
data(){
return {
categories:[
{id:'aa',name:'热门推荐'},
{id:'bb',name:'手机数码'},
{id:'cc',name:'家用电器'},
{id:'dd',name:'电脑显卡'},
]
}
},
methods:{
//创建子组件向父组件传值的事件响应函数
btnClick(item){
//向父组件传值,定义一个子组件发出的自定义事件(itemClick)
//第一个参数是自定义事件名
//第二个参数是发射出去的信息,传给父组件的监听函数
this.$emit('item-click',item)
}
}
}
var app=new Vue({
el:"#app",
data:{
},
methods:{
//创建父组件的监听函数,item是子组件$emit()传递过来的值
cpnClick(item){
console.log(item.name);
}
},
components:{
'cpn':sonCpn//这里也可以直接按es6的写法,写为cpn
}
}
);
</script>
</body>
6、子组件内部双向数据绑定
- 1、子组件内要创建data进行绑定
data(){
return{
//通过vue实例的data来改变子组件里的值
dnumber1:this.number1,
dnumber2:this.number2
}
}
- 2、子组件内要创建data进行绑定
在子组件中进行v-model双向绑定,不要在父组件中进行双向数据绑定
模板中
<template id="cpn">
<div>
<h2>props:{{number1}}</h2>
<h2>data:{{dnumber1}}</h2>
<input type="text" v-model="dnumber1"/>
<h2>props:{{number2}}</h2>
<h2>data:{{dnumber2}}</h2>
<input type="text" v-model="dnumber2"/>
</div>
</template>
组件定义中
components:{
'cpn':{
template:"#cpn",
props:{
number1:Number,
number2:Number
},
data(){
return{
//通过vue实例的data来改变子组件里的值
dnumber1:this.number1,
dnumber2:this.number2
}
}
}
},
- 3、子组件模板内进行双向绑定
通过:value=“dnumber1”进行单向绑定(父传子)
通过:input=‘自定义事件处理函数’,进行反向数据帮当
完整代码(子传父)
<template id="cpn">
<div>
<h2>props:{{number1}}</h2>
<h2>data:{{dnumber1}}</h2>
<!--子组件模板内绑定数据,:value实现单向绑定(父传子),@input实现反向绑定(通过事件子传父)-->
<input type="text" :value="dnumber1" @input='nubmer1input'/>
<h2>props:{{number2}}</h2>
<h2>data:{{dnumber2}}</h2>
<input type="text" :value="dnumber2" @input='nubmer2input'/>
</div>
</template>
- 4、子组件模板内进行数据和事件回调方法逻辑的编写
components:{
'cpn':{
template:"#cpn",
props:{
number1:Number,
number2:Number
},
data(){
return{
//通过vue实例的data来改变子组件里的值
dnumber1:this.number1,
dnumber2:this.number2
}
},
methods:{
nubmer1input(event){
this.dnumber1=event.target.value
},
nubmer2input(event){
this.dnumber2=event.target.value
}
},
}
},
- 5、继续改造子组件,将子组件传来的信息传出去(传到父组件去)
methods:{
nubmer1input(event){
this.dnumber1=event.target.value;
this.$emit('num1change',this.dnumber1);
},
nubmer2input(event){
this.dnumber2=event.target.value;
this.$emit('num2change',this.dnumber2);
}
},
- 6、在父组件中监听从子组件发过来的值
<!--父组件模板-->
<div id="app">
<!--父组件监听子组件传来的值,通过自定义的num1change事件来调用num1change获得子组件传来的值-->
<cpn :number1="num1" :number2="num2" @num1change='num1change' @num1change='num2change'></cpn>
</div>
- 6、在父组件中实现自定义监听事件传来的值,调用自定义处理函数
methods:{
num1change(value){
this.num1=value;
},
num1change(value){
this.num2=value;
},
},
- 7、进一步修改自定义方法,让连个数值框出现某种倍数关系
methods:{
nubmer1input(event){
//1、将input中的vlaue复制到dnumber中
this.dnumber1=event.target.value;
//2、为了让父组件可以修改值,发出一个事件
this.$emit('num1change',this.dnumber1);
//3、同时修改dnumber2的值
this.dnumber2=this.dnumber1*100
this.$emit('num2change',this.dnumber2);
},
nubmer2input(event){
this.dnumber2=event.target.value;
this.$emit('num2change',this.dnumber2);
this.dnumber1=this.dnumber2/100;
this.$emit('num1change',this.dnumber1);
}
},
完整案例代码
<!DOCTYPE html>
<html>
<head>
<script src="vue.js"></script>
<style type="text/css">
</style>
</head>
<body>
<!--父组件模板-->
<div id="app">
<!--父组件监听子组件传来的值,通过自定义的num1change事件来调用num1change获得子组件传来的值-->
<cpn :number1="num1"
:number2="num2"
@num1change='num1change'
@num2change='num2change'>
</cpn>
</div>
<!--子组件模板-->
<template id="cpn">
<div>
<h2>props:{{number1}}</h2>
<h2>data:{{dnumber1}}</h2>
<!--子组件模板内绑定数据,:value实现单向绑定(父传子),@input实现反向绑定(通过事件子传父)-->
<input type="text" :value="dnumber1" @input='nubmer1input'/>
<h2>props:{{number2}}</h2>
<h2>data:{{dnumber2}}</h2>
<input type="text" :value="dnumber2" @input='nubmer2input'/>
</div>
</template>
<script type="text/javascript">
var app=new Vue({
el:"#app",
data:{
num1:1,
num2:0
},
methods:{
num1change(value){
this.num1=parseFloat(value);
},
num2change(value){
this.num2=parseFloat(value);
},
},
components:{
'cpn':{
template:"#cpn",
props:{
number1:Number,
number2:Number
},
data(){
return{
//通过vue实例的data来改变子组件里的值
dnumber1:this.number1,
dnumber2:this.number2
}
},
methods:{
nubmer1input(event){
//1、将input中的vlaue复制到dnumber中
this.dnumber1=event.target.value;
//2、为了让父组件可以修改值,发出一个事件
this.$emit('num1change',this.dnumber1);
//3、同时修改dnumber2的值
this.dnumber2=this.dnumber1*100
this.$emit('num2change',this.dnumber2);
},
nubmer2input(event){
this.dnumber2=event.target.value;
this.$emit('num2change',this.dnumber2);
this.dnumber1=this.dnumber2/100;
this.$emit('num1change',this.dnumber1);
}
},
}
},
}
);
</script>
</body>
</html>
7、父子组件的访问方式$children及 $refs
- 有时候需要使用父组件直接访问子组件,子组件直接访问父主键,或者子组件之间访问
父访问子:使用$children或 $refs(引用)
子访问父:使用 $parent - $children 的访问
this. $children是一个数组类型,包含所有子组件对象
通过遍历取出所有子组件的message状态
使用$children[n].拿到子组件
<body>
<!--父组件模板-->
<div id="app">
<cpn ></cpn>
<button @click='btnclick'>打印子组件message</button>
</div>
<!--子组件模板-->
<template id="cpn">
<div>我是子组件 </div>
</template>
<script type="text/javascript">
var app=new Vue({
el:"#app",
data:{
message:'hello world'
},
methods:{
btnclick(){
this.$children[0].showMessage();
console.log(this.$children[0].name);
}
},
components:{
'cpn':{
template:"#cpn",
data(){
return{
name:'我是子组件的属性'
}
},
methods:{
showMessage(){
console.log('show son component message');
}
}
}
},
}
);
</script>
</body>
使用下标值去拿对象不是特别好,有可能拿错,可以通过$refs去拿
- 1、在html使用组件时对自定义组件设置一个属性(customProp)
<div id="app">
<cpn ref='customProp'></cpn>
<button @click='btnclick'>打印子组件message</button>
- 2、取值路径为:
this.$refs.customProp.name
完整代码为:
<body>
<!--父组件模板-->
<div id="app">
<cpn ref='customProp'></cpn>
<button @click='btnclick'>打印子组件message</button>
</div>
<!--子组件模板-->
<template id="cpn">
<div>我是子组件 </div>
</template>
<script type="text/javascript">
var app=new Vue({
el:"#app",
data:{
message:'hello world'
},
methods:{
btnclick(){
console.log(this.$refs.customProp.name);
}
},
components:{
'cpn':{
template:"#cpn",
data(){
return{
name:'我是子组件的属性'
}
},
methods:{
showMessage(){
console.log('show son component message');
}
}
}
},
}
);
</script>
</body>
8、子组件访问父组件(少用)$parent
<body>
<!--父组件模板-->
<div id="app">
<cpn></cpn>
</div>
<!--子组件模板-->
<template id="cpn">
<div>
<div>我是子组件 </div>
<button @click='btnclick'>按钮</button>
</div>
</template>
<script type="text/javascript">
var app=new Vue({
el:"#app",
data:{
message:'hello world'
},
methods:{
},
components:{
'cpn':{
template:"#cpn",
data(){
return{
name:'我是子组件的属性'
}
},
methods:{
btnclick(){
//1、访问父组件
console.log(this.$parent);
//访问根组件
console.log(this.$root);
}
}
}
},
}
);
</script>
</body>