想总结一下组件中传递数据的方法。
父组件向子组件传数据
这种应该是 vue.js 中最常见也是最为自然的一种方式了,要求我们在子组件中声明 props 然后在父组件中为子组件的 prop 赋值。
1、第一步在子组件中声明 props 为父组件传递数据做好准备工作
代码语言:javascript复制<template>
<ul>
<li v-for="person in persons" :key="person.id"> name={{person.name}} gender={{person.gender}} </li>
</ul>
</template>
<script>
export default {
name:'Child',
props:['persons']
}
</script>
2、在父组件中通过 prop 传递值给子组件
代码语言:javascript复制<template>
<div>
<Child :persons="persons" />
</div>
</template>
<script>
import Child from './Child.vue'
export default {
components: { Child },
name:'Parent',
data() {
return {
persons:[
{id:'001',name:'张三',gender:'男'},
{id:'002',name:'李四',gender:'男'},
{id:'003',name:'王五',gender:'男'},
]
}
}
}
</script>
子组件向父组件传递数据 - props 实现
通过 props 传递进来的数据,不允许在子组件里更改的,如果强行更改的话 Vue.js 会报错。OOP 的原则,哪个对象的数据就应该调用哪个对象的方法来管理,在 Vue.js 中也是这样。
那现在就变成了怎么调用到父组件的方法了,办法说来也简单那不就是,把父组件的方法通过 props 传递给子组件,这样子组件就能调用到了。
1、第一步 在子组件中定义用于传方法的 prop
代码语言:javascript复制<template>
<div>
<ul>
<li v-for="person in persons" :key="person.id"> name={{person.name}} gender={{person.gender}} </li>
</ul>
<span>name:</span><input type="text" v-model="name"/><br>
<span>gender:</span><input type="text" v-model="gender"/><br>
<button @click="addPerson">增加</button>
</div>
</template>
<script>
export default {
name:'Child',
props:['persons','addPersonCallBack'], // 添加用户调用父组件方法的 prop -- addPersonCallBack
data(){
return {
name:'',
gender:''
}
},
methods:{
addPerson(){
if(this.name == ''){
return
}
else if(this.gender == ''){
return
}
let person = {
name:this.name,
gender:this.gender
}
this.addPersonCallBack(person)
this.name = ''
this.gender = ''
}
}
}
</script>
2、父组件把更新数据的方法传给子组件
代码语言:javascript复制<template>
<div>
<Child :persons="persons" :addPersonCallBack="addPerson"/>
</div>
</template>
<script>
import Child from './Child.vue'
export default {
components: { Child },
name:'Parent',
data() {
return {
persons:[
{id:'001',name:'张三',gender:'男'},
{id:'002',name:'李四',gender:'男'},
{id:'003',name:'王五',gender:'男'},
]
}
},
methods:{
addPerson(person) {
this.persons.push(person)
}
}
}
</script>
工作正常。
子组件向父组件传递数据 - 自定义事件实现
这个实现和 props 在代码上差不多,props 实现是在子组件里直接调用父组件的函数。事件的实现手法是子组件触发事件,并配置好对应的参数值,父组件只要注册好事件的监听就行了。
1、子组件的代码如下
代码语言:javascript复制<template>
<div>
<ul>
<li v-for="person in persons" :key="person.id"> name={{person.name}} gender={{person.gender}} </li>
</ul>
<span>name:</span><input type="text" v-model="name"/><br>
<span>gender:</span><input type="text" v-model="gender"/><br>
<button @click="addPerson">增加</button>
</div>
</template>
<script>
export default {
name:'Child',
props:['persons'],
data(){
return {
name:'',
gender:''
}
},
methods:{
addPerson(){
if(this.name == ''){
return
}
else if(this.gender == ''){
return
}
let person = {
name:this.name,
gender:this.gender
}
// 只用改这一行,把之前的函数调用,改成事件触发
this.$emit('addPersonEvent',person)
this.name = ''
this.gender = ''
}
}
}
</script>
2、父组件的代码
代码语言:javascript复制<template>
<div>
<Child :persons="persons" @addPersonEvent="addPerson"/> <!-- 注册事件监听-->
</div>
</template>
<script>
import Child from './Child.vue'
export default {
components: { Child },
name:'Parent',
data() {
return {
persons:[
{id:'001',name:'张三',gender:'男'},
{id:'002',name:'李四',gender:'男'},
{id:'003',name:'王五',gender:'男'},
]
}
},
methods:{
addPerson(person) {
this.persons.push(person)
}
}
}
</script>
代码语言:javascript复制<template>
<div>
<Child :persons="persons" ref="child"/> <!-- 注册事件监听-->
</div>
</template>
<script>
import Child from './Child.vue'
export default {
components: { Child },
name:'Parent',
data() {
return {
persons:[
{id:'001',name:'张三',gender:'男'},
{id:'002',name:'李四',gender:'男'},
{id:'003',name:'王五',gender:'男'},
]
}
},
methods:{
addPerson(person) {
this.persons.push(person)
}
},
mounted(){
this.$refs.child.$on('addPersonEvent',this.addPerson)
}
}
</script>
数据传递集大成者 - 全局事件总线
通过前面用 js 实现监听的例子我们发现,我们只要在事件的生产者上调用 emit 方法,事件消费者通过 on 主动的注册事件回调,就能完成数据传递了。
现在有两个事实,1 Vue 实例身上一定有 emit 和 on 方法,2、只要把 Vue 实例的引用添加到 Vue.prototype 的属性上,那么所有的组件都能用到 emit 和 on 方法。这样做的话事件的触发和回调都集中了。
1、改一下 main.js 的写法
代码语言:javascript复制import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render: h => h(App),
beforeCreate(){
Vue.prototype.$bus = this; // 给它把引用加上去
}
}).$mount('#app')
2、触发的代码要改一下
代码语言:javascript复制<template>
<div>
<ul>
<li v-for="person in persons" :key="person.id"> name={{person.name}} gender={{person.gender}} </li>
</ul>
<span>name:</span><input type="text" v-model="name"/><br>
<span>gender:</span><input type="text" v-model="gender"/><br>
<button @click="addPerson">增加</button>
</div>
</template>
<script>
export default {
name:'Child',
props:['persons'],
data(){
return {
name:'',
gender:''
}
},
methods:{
addPerson(){
if(this.name == ''){
return
}
else if(this.gender == ''){
return
}
let person = {
name:this.name,
gender:this.gender
}
// 只用改这一行,把之前的函数调用,改成事件触发
this.$bus.$emit('addPersonEvent',person)
this.name = ''
this.gender = ''
}
},
beforeDestroy(){
this.$bus.off("addPersonEvent")
}
}
</script>
3、注册回调的代码也要改一下
代码语言:javascript复制<template>
<div>
<Child :persons="persons" ref="child"/> <!-- 注册事件监听-->
</div>
</template>
<script>
import Child from './Child.vue'
export default {
components: { Child },
name:'Parent',
data() {
return {
persons:[
{id:'001',name:'张三',gender:'男'},
{id:'002',name:'李四',gender:'男'},
{id:'003',name:'王五',gender:'男'},
]
}
},
methods:{
addPerson(person) {
this.persons.push(person)
}
},
mounted(){
this.$bus.$on('addPersonEvent',this.addPerson)
}
}
</script>
这样做的好处是完全不用在乎组件父子之间的关系,任何两个组件之间都能传递数据了。
总结
组件间关系 | 适合的传递类型 |
---|---|
父给子传 | props |
子给父传 | 自定义事件 |
其它 | 全局事件总线 |