组件间的数据交互方式(父子、兄弟、非亲)

一、前言

        前端开发中组件的封装是必不可少的,高效的组件数据交互能够减少重复代码的生成与用户体验的提升。本文将分以下三种情况去分析组件间的数据交互方式:

1、父子组件间的数据交互(props <---> $emit、$refs <---> $parent);

2、兄弟组件间的数据交互(this.$parent.$refs);

3、非亲组件(两个组件之间没有任何关联)间的数据交互(window['methodName'] = (event, params) => { },window.parent.methodName(event))。

二、父子组件数据交互

<-- 父组件 Person.vue -->
<template>
    <div>
        <el-form ref="personRef" :model="person">
            <el-form-item prop="id">
                <el-input v-model="person.id" clearable></el-input>
            </el-form-item>
            <el-form-item prop="name">
                <el-input v-model="person.name" clearable></el-input>
            </el-form-item>
            <el-form-item prop="age">
                <el-input v-model="person.age" clearable></el-input>
            </el-form-item>
            <el-form-item>
                <el-input v-model="approveInfo" disabled></el-input>
            </el-form-item>
        </el-form>
        <el-button @click="this.showDialog = true">提交</el-button>
        <person-approve-dialog 
            ref="personApproveRef" 
            v-if="showDialog" 
            :personInfo="person"
            @getApproveComment="getApproveComment"
        >
        </person-approve-dialog>      
    </div>
</template>

<script>
import PersonApproveDialog from '@/components/PersonApproveDialog.vue'
export default {
    name: 'Person',
    components: { PersonApproveDialog },
    data() {
        return {
            showDialog: false,
            approveInfo: '',
            person: {
                id: '',
                name: '',
                age: ''
            }
        }
    },
    methods: {
        getApproveComment(value) {
            this.approveInfo = value;
            if (this.$refs['personApproveRef'].approveComment.lenght > 100) {
                this.$message(message: '审批内容超长', type: 'error')
            }
            this.$nextTick(() => {
                this.showDialog = false
            })
        }
    }
}
</script>
<-- 子组件 PersonApproveDialog.vue -->
<template>
    <el-dialog :visible.sync="visible">
        <el-form ref="personApproveRef">
            <el-form-item>
                <el-input v-model="personInfo.id" disabled></el-input>
            </el-form-item>
            <el-form-item>
                <el-input v-model="personInfo.name" disabled></el-input>
            </el-form-item>
            <el-form-item>
                <el-input v-model="personInfo.age" disabled></el-input>
            </el-form-item>
            <el-form-item prop="approveComment">
                <el-input v-model="approveComment" clearable></el-input>
            </el-form-item>
        </el-form>
        <el-button @click="checkValid()">核验</el-button>
        <el-button @click="close()">关闭</el-button>
    </el-dialog>
</template>

<script>
export default {
    name: 'PersonApproveDialog',
    props: { personInfo: Object },
    data() {
        return {
            visible: true,
            approveComment: ''
        }
    },
    methods:{
        checkValid() {
            if (this.$parent.person.id === '') {
                this.$message(message: '人员 id 必填', type: 'error')
            }
        },
        close() {
            this.visible = false,
            this.$emit('getApproveComment', this.approveComment)
        }
    }
}
</script>

分析:

(1)子组件获取父组件的数据:

方法一:通过在子组件中添加 props 属性来获取父组件的数据,如上图代码 :personInfo="person",父组件的 person 对象赋值给子组件的 personInfo 属性,子组件从 personInfo 中即可拿到父组件的人员信息数据。

方法二:通过 this.$parent 主动去获取父组件的数据信息,this.$parent 表示获取当前子组件的父组件信息(包含父组件的变量和方法),案例中的 this.$parent.person.id 取的就是父组件中的变量(人员 id 值)。

(2)父组件获取子组件的数据:

方法一:通过 $emit 来传递数据,图中 this.$emit('getApproveComment', this.approveComment) 表示触发 getApproveComment 事件,并将子组件的 approveComment 栏位值传过去,父组件根据这个事件去执行自己的方法 getApproveComment(value),其中 value 就是子组件传过来的approveComment 的值。 

方法二:通过 this.$refs 主动去获取子组件的数据信息,图中this.$refs['personApproveRef'].approveComment 即可获取到 personApproveRef 对应的子组件的审批内容。

三、兄弟组件数据交互

<-- 父组件 Address.vue -->
<template>
    <div>
        <el-form>
            <el-form-item prop="address">
                <el-input v-model="address" clearable></el-input>
            </el-form-item>
        </el-form>
        <province-dialog ref="provinceRef"></province-dialog>
        <city-dialog ref="cityRef"></city-dialog>   
    </div>
</template>

<script>
import ProvinceDialog from '@/components/ProvinceDialog.vue'
import CityDialog from '@/components/CityDialog.vue'
export default {
    name: 'Address',
    components: { ProvinceDialog, CityDialog },
    data() {
        return {
            address: ''
        }
    }
}
</script>
<-- 子组件 ProvinceDialog.vue -->
<template>
    <el-dialog :visible.sync="visible">
        <el-form>
            <el-form-item prop="provinceName">
                <el-input 
                    v-model="provinceName" 
                    clearable 
                    @change="changeProvince">
                </el-input>
            </el-form-item>
        </el-form>    
    </el-dialog>
</template>

<script>
export default {
    name: 'ProvinceDialog',
    data() {
        return {
            visible: true,
            provinceName: ''
        }
    },
    methods: {
        changeProvince(value) {
            if (this.$parent.$refs['cityRef'].cityName === '') {
                this.$message(message: '城市名为空,无法获取详细地址', type: 'error')
            } else {
                this.$parent.address = value + this.$parent.$refs['cityRef'].cityName
            }
        }
    }
}
</script>
<-- 子组件 CityDialog.vue -->
<template>
    <el-dialog :visible.sync="visible">
        <el-form>
            <el-form-item prop="cityName">
                <el-input v-model="cityName" @change="changeCity"></el-input>
            </el-form-item>
        </el-form>
    </el-dialog>
</template>

<script>
export default {
    name: 'CityDialog',
    data() {
        return {
            visible: true,
            cityName: ''
        }
    },
    methods: {
        changeCity(value) {
            if (this.$parent.$refs['provinceRef'].provinceName === '') {
                this.$message(message: '省份为空,无法获取详细地址', type: 'error')
            } else {
                this.$parent.address = this.$parent.$refs['provinceRef'].provinceName 
                 + value
            }
        }
    }
}
</script>

分析:

        一个父组件引用了多个子组件,那么这多个子组件间就称之为兄弟组件,它们之间的数据交互主要是通过父组件(this.$parent)来进行, 父组件在其中充当着数据传递的桥梁作用。如代码所示,一个子组件存放省份信息,另一个子组件存放城市信息,只要省份和城市任何一个改变了,那么由二者拼接而成的地址也就会变化,this.$parent.$refs['provinceRef'].provinceName 就是先通过 this.$parent 获取父组件信息,然后通过 $refs['provinceRef'] 获取父组件的子组件信息,从而拿到兄弟组件 ProvinceDialog.vue 的省份名称,同理 this.$parent.$refs['cityRef'].cityName 也是先通过 this.$parent 获取父组件信息,然后通过 $refs['cityRef'] 获取父组件的子组件信息,从而拿到兄弟组件 CityDialog.vue 的城市名称,进而拼接成详细的地址信息。

四、非亲组件数据交互

<-- 组件 ProvinceDialog.vue -->
<template>
    <el-dialog :visible.sync="visible">
        <el-form>
            <el-form-item prop="provinceName">
                <el-input 
                    v-model="provinceName" 
                    clearable 
                    @change="changeProvince">
                </el-input>
            </el-form-item>
        </el-form>    
    </el-dialog>
</template>

<script>
export default {
    name: 'ProvinceDialog',
    data() {
        return {
            visible: true,
            provinceName: ''
        }
    },
    mounted() {
        let _this = this;
        window['getProvinceName'] = (event) => { return _this.provinceName }
    },
    methods: {
        changeProvince(value) {
            if (window.parent.getCityName && window.parent.getCityName(event) 
            === '') {
                this.$message(message: '城市为空,无法获取详细地址', type: 'error')
            }
        }
    }
</script>
<-- 组件 CityDialog.vue -->
<template>
    <el-dialog :visible.sync="visible">
        <el-form>
            <el-form-item prop="cityName">
                <el-input v-model="cityName" clearable @change="changeCity"></el-input>
            </el-form-item>
        </el-form>
    </el-dialog>
</template>

<script>
export default {
    name: 'CityDialog',
    data() {
        return {
            visible: true,
            cityName: ''
        }
    },
    mounted() {
        let _this = this;
        window['getCityName'] = (event) => { return _this.cityName }
    },
    methods: {
        changeCity(value) {
            if (window.parent.getProvinceName && window.parent.getProvinceName(event) 
            === '') {
                this.$message(message: '省份为空,无法获取详细地址', type: 'error')
            }
        }
    }
}
</script>

分析:

        如果两个组件未在同一个父组件中引用,那么称这两个组件为非亲组件,即它们没有任何直接的联系,它们之间的数据交互可以先定义一个获取数据的方法:window['methodName'] ,然后在需要获取数据的组件中使用该方法:window.parent.methodName(),这种方式存在一个弊端就是定义数据获取方法 methodName 所在的组件需要使用过(至少进入生命周期 mounted 一次),否则 window.parent.methodName 为 undefined,所以需要注意定义数据获取方法的位置,可以在app.vue 中定义,也可以在 main.js 中,根据自身所需去写就好。除了这种方法,其实还有一种使用 inject provide 的方式,此类方式本文不做赘述。

  • 20
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

【金融科技蚂蚁】

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值