vue: 从一个下拉框组件理解vue中的父子通讯

5,347 阅读5分钟

如果说vue组件化开发中第一步应该了解的是什么的话,那无疑是父子组件之间是如何实现通讯的(说白了就是父子组件中数据是如何传递的),只有理解了这一步,才能更好的开发组件

这里先提出两个关键词: propsemit

写这个组件之前,先看看效果图:

下拉框组件图

组件开发分析:

既然是组件:

  • 首先组件内部数据内容肯定是可变的(如上图中的"按时间排序"之类的),这必须由父组件传入(即父组件如何将数据传个父组件);
  • 在选择了内容之后,如何将数据传出来(即子组件如何将数据传给父组件)

先写结构:

父组件

<!--下拉框父组件-->
<template>
    <div id="app">
        <oSelect @changeOption="onChangeOption" :selectData="selectData"></oSelect>
        <!--
        selectData: 传入父组件需要传入的数据;格式:childDataName="parentDataName";
        onChangeOption: 子组件触发的事件名,通过触发一个事件从而来接收子组件的传过来的数据
        格式:@childEventName="parentEventName"
        注:可写多个
        -->
    </div>
</template>
<script>
import oSelect from "@/components/select.vue"; //引入组件
export default{
    name: 'App',
    data(){
        return {
            selectData: {
                defaultIndex: 0, //默认选中的是第几个
                selectStatus: false, // 通过selectStatus来控制下拉框的显示/隐藏
                selectOptions: [ // 下拉框中的数据 name这样的参数,看项目是否有需求,可自行修改
                    {
                        name: 'time',
                        context: '按时间排序'
                    },
                    {
                        name: 'view',
                        context: '按浏览量排序'
                    },
                    {
                        name: 'like',
                        context: '按点赞数排序'
                    },
                    {
                        name: 'reply',
                        context: '按回复数排序'
                    },
                    {
                        name: 'reward',
                        context: '按打赏数排序'
                    }
                ]
            }
        }
    },
    methods:{
        onChangeOption(index){
        //子组件通过一个事件来触发onChangeOption方法,从而传递一系列参数,这里的index就是传过来的
            this.selectData.defaultIndex = index;
        //触发过后,动态改变了需要值    
        }
    },
    components: {
        oSelect,
        //注册组件
    }
}
</script>

子组件

<template>
<!-- 下拉框组件html结构(子组件) -->
<div class="select-box" @click="changeStatus">
<!-- changeStatus事件: 点击实现下拉框的显示和隐藏  -->
<h3 class="select-title"
	:name="selectData.selectOptions[selectData.defaultIndex].name"
	:class="{'select-title-active': selectData.selectStatus}"> 
	<!--属性name class的动态绑定-->
	{{ selectData.selectOptions[selectData.defaultIndex].context }} 
	<!--这里主要绑定选择的数据-->
</h3>
<transition name="slide-down">
<!--transition 实现下拉列表显示隐藏时的动画-->
<ul class="select-options" v-show="selectData.selectStatus">
    <li class="select-option-item" 
    	v-for="(item,index) in selectData.selectOptions"
    	@click="EmitchangeOption(index)"
    	:class="{'select-option-active':selectData.defaultIndex===index}">
    	<!--
    	    v-for:循环数据渲染下拉列表
    	    EmitchangeOption:点击下拉列表事件
    	    class:动态绑定被选中的数据
    	-->
    	{{ selectData.selectOptions[index].context }}
    	
    </li>
    <div class="arrow-top"></div>
</ul>	
</transition>	
</div>    
</template>
<script>
export default{
    name: 'oSelect', //建议大家都写上这个,有利于我们知道这个组件叫什么名字
    //通过props来接收父组件传过来的数据
    props:{
    	selectData: {
    		type: Object //规定传过来的数据为对象,否则就会报错(其实这样写就是规避错误和良好的习惯)
    	}
    },
    methods:{
    	EmitchangeOption(index){
    		this.$emit('changeOption',index);
    	    // 通过点击事件触发EmitchangeOption函数,传入当前点击下拉列表中的索引值index
    	    // 下拉框通过emit方法触发父组件中changeOption函数,动态传给父组件需要的数据,这里为索引值
    	},
    	changeStatus(){
    	    // 通过changeStatus事件动态改变selectStatus的值,从而控制下拉框的显示隐藏
    		this.selectData.selectStatus = !this.selectData.selectStatus
    	}
    }
}
</script>

总结

  • 从以上的示例可以看出来,父组件传入数据,需要在父组件中线绑定一个属性,挂载需要传入的数据;
  • 子组件接收父组件的数据通过 props 方法来接收;
  • 子组件传递数据需要使用 emit 方法来绑定父组件中事先设定好的方法,从而动态传递操作后需要的数据

最终效果如下:

动态效果图

附上组件中的css,仅供参考:

.select-box{
	position: relative;
	max-width: 250px;
	line-height: 35px;
	margin: 50px auto;
}
.select-title{
	position: relative;
	padding: 0 30px 0 10px;
	border: 1px solid #d8dce5;
	border-radius: 5px;
	transition-duration: 300ms;
	cursor: pointer;
}
.select-title:after{
	content: '';
	position: absolute;
	height: 0;
	width: 0;
	border-top: 6px solid #d8dce5;
	border-left: 6px solid transparent;
	border-right: 6px solid transparent;
	right: 9px;
	top: 0;
	bottom: 0;
	margin: auto;
	transition-duration: 300ms;
	transition-timing-function: ease-in-out;
}
.select-title-active{
	border-color: #409eff;
}
.select-title-active:after{
	transform: rotate(-180deg);
	border-top-color: #409eff;
}
.select-options{
	position: absolute;
	padding:10px 0;
	top: 45px;
	border:1px solid #d8dce5;
	width: 100%;
	border-radius: 5px;
}
.select-option-item{
	padding:0 10px;
	cursor: pointer;
	transition-duration: 300ms;
}
.select-option-item:hover,.select-option-active{
	background: #f1f1f1;
	color: #409eff;
}

<!--箭头css-->
.arrow-top{
	position: absolute;
	height: 0;
	width: 0;
	top: -7px;
	border-bottom: 7px solid #d8dce5;
	border-left: 7px solid transparent;
	border-right: 7px solid transparent;
	left: 0;
	right: 0;
	margin: auto;
	z-index: 99;
}
.arrow-top:after{
	content: '';
	position: absolute;
	display: block;
	height: 0;
	width: 0;
	border-bottom: 6px solid #fff;
	border-left: 6px solid transparent;
	border-right: 6px solid transparent;
	left: -6px;
	top: 1px;
	z-index: 99;
}

<!--下拉框显示隐藏动画-->
.slide-down-enter-active,.slide-down-leave{
	transition: all .3s ease-in-out;
	transform-origin:0 top;
	transform: scaleY(1);
}
.slide-down-enter{
	transform: scaleY(0);
}
.slide-down-leave-active{
	transition: all .3s ease-in-out;
	transform-origin:0 top;
	transform: scaleY(0);
}

好了,关于本文就结束了,感谢大家的阅读,如有不正之处,还望指正,非常感谢,想要了解更多的前端方面相关的知识, 请搜索关注微信公众号【大前端js,了解更多前端的硬技术

原创不易,转载请注明出处,铭感五内