一、前言
在Vue开发过程中核心其实就是组件化开发,Vue是有一个个不同的组件组合而成。但每个组件相互独立的,这意味着不同组件之间的数据是不能直接相互引用的,但在实际的开发过程中又需要访问其他组件里的数据,这样就有了组件之间通信的问题。接下来就具体看一下他们是怎么实现的吧。
二、父组件向子组件传递数据
首先我们创建一个名为shool-name子组件,并在根组件上定义一个数据schoolName。我们想把schoolName数据传递给子组件进行渲染出来该怎么做呢?首先我们需要在子组件的配置对象上添加一个props属性。props属性值是一个数组,它用来存放父组件传递过来的数据,子组件通过props的值可以获取传递过来的数据进行使用。
我们知道了子组件怎么获取父组件传递的数据后,还要知道的是父组件是怎么把数据传递过来的。其实很简单,只需要在使用子组件的时候为该组件自定义一个属性名,属性值为要传递的数据。这里需要注意的是:父组件传递数据时自定义的属性名和子组件的props属性中要接收数据的子元素名要保持一致。这样就实现了父组件向子组件传递数据了,具体实现如下代码:
<div id="app">
<school-name :name="schoolName"></school-name>
</div>
// 子组件
Vue.component('school-name',{
props: ['name'],
template:
`<div>
<li>{{name}}</li>
</div>`
});
// 根组件
let vm = new Vue({
el:'#app',
data:{
schoolName:'QH'
}
});
我们可以看到成功的把父组件的数据传递给子组件给渲染出来了:
刚刚传递的是一个字符串数据,那可不可以传递数组、对象等数据呢?其实是可以的,不信我们就试试看。我们重新创建一个子组件并修改要传递的数据,修改后的代码如下:
// 使用组件
<div id="app">
<ul>
<schoolInformation v-for="(item, index) in school" :information="item" :index="index"></shoolInformation>
</ul>
</div>
// 子组件
Vue.component('schoolInformation',{
props: ['information','index'],
template:
`<div>
<li>index:{{index}}, schoolName:{{information.name}},address:{{information.address}}</li>
</div>`
});
// 根组件
let vm = new Vue({
el:'#app',
data:{
school: [
{
'name':'清华大学',
'address':'北京市海淀区双清路30号'
},
{
'name':'复旦大学',
'address':'上海市杨浦区邯郸路220号'
},
{
'name':'浙江大学',
'address':'杭州市西湖区余杭塘路866号 '
}
]
}
});
打开浏览器后咋啥都没有?难道不可以传递数组吗?我们打开控制台看一下有没有报什么错误。打开后可以看到这样的错误:
哦,它提示我们未正确注册组件。我们在检查一下我们的代码看看呢,我们定义组件的时候组件名是用的驼峰命名法,使用的组件的时候也用的驼峰命名法命名的组件名,这样是不对的。其实Vue规定了定义组件时若使用驼峰命名法命名组件,在使用组件的时候不能用驼峰命名的组件名,应当把大写的字母不小写并用中划线来连接,否则不能正确注册组件。我们修改引用组件代码如下:
<shool-information v-for="(item, index) in school" :information="item" :index="index"></shool-information>
这时我们可以看到父组件向子组件传递的数据就被渲染了出来,说明传递数组类型的数据是没毛病的。并且我们可以看到父组件不止传递了一个数据,还传递了遍历数组时的索引值,说明父向子传递数据同时可传递多个,只需在子组件的props属性中添加要传值时自定义的属性名就可以了。同时我们应当注意在创建子组件并命名时,尽量不采用驼峰命名,以避免不必要的麻烦。
三、子组件向父组件传递数据
刚刚我们成功的把父组件的数据传递给子组件,但现在我想给子组件添加一个按钮选择学校,当点击选择学校按钮的后父组件显示当前选择的是哪个学校的,那又该如何实现呢?这个需求的实质就是把子组件的数据传递给父组件,下面代码就来实现它吧
// 使用组件
<div id="app">
<ul>
// 绑定自定义的触发事件 cschool
<shool-information @cschool="changeEvent" v-for="(item, index) in school" :key="index" :information="item" :index="index"></shool-information>
<p>
你选择的学校是:<strong>{{schoolName}}</strong>
</p>
</ul>
</div>
// 子组件
Vue.component('shoolInformation',{
props: ['information','index'],
template:
`<div>
<li>index:{{index}}, schoolName:{{information.name}},address:{{information.address}}</li>
<button @click="chooseEvent(information.name)">{{information.name}}</button>
</div>`,
methods: {
chooseEvent(schoolName){
// 自定义触发事件,实现数据的传值
this.$emit('cschool',schoolName);
}
},
});
// 根组件
let vm = new Vue({
el:'#app',
data:{
school: [
{
'name':'清华大学',
'address':'北京市海淀区双清路30号'
},
{
'name':'复旦大学',
'address':'上海市杨浦区邯郸路220号'
},
{
'name':'浙江大学',
'address':'杭州市西湖区余杭塘路866号 '
}
],
schoolName:''
},
methods: {
changeEvent(data){
console.log(data);
this.schoolName = data;
}
},
});
在子组件中添加一个按钮,为按钮绑定点击事件chooseEvent,并在子组件中实现这个方法。要想把子组件的数据传递给父组件就需要在点击事件的方法中自定义一个事件,把要传递的数据通过参数的形式传递给父组件,父组件绑定自定义的事件就可以拿到传递的数据。当我们触发子组件点击事件时,这时的this指向的是子组件对象,在子组件的原型对象上有一个**$emit()**方法可以用来自定义触发事件。这个方法的第一个参数是自定义事事件的事件名,第二个参数就是要传递给父组件的数据。
这里我们在子组件的点击事件中通过**$emit()方法自定义了一个名为cschool的触发事件,并把要触底的数据当做事件的参数。自定义好触发事件后我们就在应用子组件时绑定这个事件。这里是在根组件中使用的子组件,绑定的事件方法当然也是在根组件中实现。在根组件中定义了一个changeEvent**方法,这个方法接收了子组件传递的数据,这里我们把传递过来的参数打印输出验证并赋值给要显示选择学校的变量。
当我们点击清华大学按钮就得到上图的结果,成功的把子组件的参数传递给了父组件。
四、组件传值之奇淫技巧
1、父组件方法传递子元素
上面我们通过子组件传递数据给父元素给来实现了在选择子组件中学校,在父组件显示已选择的学校的需求。既然子传父可以实现需求,那父传子能不能实现呢,带着这个问题我们看一下子传父的实现需求的本质是什么。子传父方式实际就是在子组件中触发点击事件,在点击事件中自定义 触发调用父组件中改变数据方法 的事件,通过这个自定义事件把要传递的数据以参数的方式传递到父组件监听的自定义事件函数中去,在这个函数中把传递过来的数据进行修改而达到目的。
既然这样那我们可不可把父组件中修改数据的方法通过父组件传值的方式传递给子组件,在子组件触发点击事件时调用这个函数呢?其实是可以的,下面通过代码来实现:
// 使用组件
<div id="app">
<ul>
// 自定义的属性 action 把父组件中的 changeEvent 函数作为属性值
<shool-information :action="changeEvent" v-for="(item, index) in school" :key="index" :information="item" :index="index"></shool-information>
<p>
你选择的学校是:<strong>{{schoolName}}</strong>
</p>
</ul>
</div>
// 子组件
Vue.component('shoolInformation',{
// 添加 action,接收父组件传递过来的数据
props: ['information','index','action'],
template:
`<div>
<li>index:{{index}}, schoolName:{{information.name}},address:{{information.address}}</li>
<button @click="chooseEvent(information.name)">{{information.name}}</button>
</div>`,
methods: {
chooseEvent(schoolName){
// 调用父组件传递过来的 changeEvent 函数
this.action(schoolName);
}
},
});
// 根组件
let vm = new Vue({
el:'#app',
data:{
school: [
{
'name':'清华大学',
'address':'北京市海淀区双清路30号'
},
{
'name':'复旦大学',
'address':'上海市杨浦区邯郸路220号'
},
{
'name':'浙江大学',
'address':'杭州市西湖区余杭塘路866号 '
}
],
schoolName:''
},
methods: {
changeEvent(data){
this.schoolName = data;
}
},
});
上面代码就是把父组件中修改数据的方法传递给子组件,在子组件中调用该方法从而达到目的。打开浏览器点击按钮,可以看到结果如上图,说明这种方法也是可以的。
2、$parent的使用
其实要想通过在组件中调用父组件修改数据的函数方法来实现需求还有更简便的方法,可以在子组件点击事件触发时通过子组件对象上的**$parent**方法就可以拿到父组件中的方法。
首先我们看一下有没有这样的方法存在,在子组件点击事件触发的时候输出this得到下图:
可以看到在组件对象下的**$parent对象下就有父组件中我们定义的changeEvent**函数,那我们就可以在子组件中直接调用了。那现在就可以子组件中的修改代码如下:
Vue.component('shoolInformation',{
// 添加 action,接收父组件传递过来的数据
props: ['information','index'],
template:
`<div>
<li>index:{{index}}, schoolName:{{information.name}},address:{{information.address}}</li>
<button @click="chooseEvent(information.name)">{{information.name}}</button>
</div>`,
methods: {
chooseEvent(schoolName){
console.log(this);
// 通过调用$parent下的changeEvent函数修改数据
this.$parent.changeEvent(schoolName);
}
},
});
这时我们再打开浏览器点击按钮选择学校得到结果如上图,看来使用这种方法同样也可以实现这个需求。
3、$root的使用
其实要实现刚刚的需求,还有一个方法。我们修改数据的方法是在根组件上的,也是当前子组件的父组件上,在子组件对象下有一个**$root对象,在这个对象下也可以拿到父组件中我们定义的changeEvent函数从而修改数据。只需将$parent换成$root进行调用函数,这里就不在演示代码了,下图是子组件$root对象上的changeEvent**函数存在的展示:
4、$childran的使用
既然子组件上有**$parent对象,那会不会父组件有$childran对象呢?其实也是有的,在父组件的$childran对象上可以拿到所有子组件对象,通过$childran对象就可以控制子组件上的数据了,但是不建议这样操作,因为要保证组件之间低耦合。通过下图就可以看到父组件$childran**对象上有其他三个子组件对象。