Vue组件学习——父子通信案例TabControl

346 阅读1分钟

TabContronl效果图

bad5ef1216292ad81e2ed0bf1633bb1.png

点击不同的选项,选项变红并底下高亮,显示对应的界面

1.结构

  • 父:App.vue 
    子:TabControl.vue
    

实现思路:

(子)TabControl.vue构建tabControl组件:

  •   1. 从App.vue文件动态获取数据("衣服","鞋子"),不能把数据写死:
          -父传子通信
              -父写在自己的动态绑定的属性attribute里,子在props属性里接收
              -子使用v-for遍历数据展示
      2.  监听点击事件,用currentIndex记录选中项的index,实现动态添加active类别、传递index到App.vue
      3.  flex布局         
                 
    

(父)App.vue使用tabControl组件、显示对应界面:

  •   1. 从TabControl.vue文件引入tab-control组件,注册组件
      2. 切换界面
          -子传父通信
              -子通过$emit,向父发送事件"btnIndex",数据index
              -利用监听事件"btnIndex",事件绑定methods,传递index,切换界面
    

2.代码细节

TabControl.vue------template主要部分

       <template v-for="(item, index) in titles" :key="item">
            <div class="tab-item" @click="btnClick(index)" :class="{ active: currentIndex == index}">
                {{item}}
            </div>
       </template>
  

这里有两个重点

  1. v-for需要绑定属性key,方便渲染

    个人理解:通过监听key有无改变,只重新渲染key发生改变的元素。不要用index作为key,因为中间插入新数据可能会改变对应的内容,key应为独一无二的标识id或者item(没有重复的情况下)都是不错的选择

  2. 动态添加active类别 active: currentIndex == index}

    active: 布尔类型,active 这个 class 存在与否将取决于是否为true。利用currentIndex运行实现动态效果

TabControl.vue------script主要部分

export default {
    props:{
        titles:{
            type: Array,
            default() {
                return{}
            }
        }
    },
    data(){
        return{
            currentIndex: 0,
        }
    },
    emits: ["btnIndex"],
    methods:{
        btnClick(index){
            this.currentIndex = index
            this.$emit("btnIndex", index)
        }
    }
    
}

细节:

  1. 子在props接收父传来的数据,在props对象里,对象声明类型和默认值,如默认值为空对象,最好不要用箭头函数,default() { return{} } 这样比较稳妥,该文章有详细的解释
  2. 最好在emits里声明了要传给父组件的事件名,在父组件里使用时才会有提示

全部代码

TabControl.vue

<template>
    <div class="tab-control">
       <template v-for="(item, index) in titles" :key="item">
            <div class="tab-item" @click="btnClick(index)" :class="{ active: currentIndex == index}">
                {{item}}
            </div>
       </template>
    </div>
</template>


<script>

export default {
    props:{
        titles:{
            type: Array,
            default() {
                return{}
            }
        }
    },
    data(){
        return{
            currentIndex: 0,
        }
    },
     emits: ["btnIndex"],
    methods:{
        btnClick(index){
            this.currentIndex = index
            this.$emit("btnIndex", index)
        }
    }
    
}
</script>


<style scoped>
    .tab-control{
        display: flex;
        text-align: center;
        line-height: 14px;
        font-size: 14px;
    }

    .tab-item{
        flex: 1;
    }

    .active{
        /* box-sizing: border-box; */
        color: red;
        border-bottom: 3px solid red;
        padding-bottom: 7px;
    }
</style>

App.vue

<template>
    <div class="app">
       <tab-control :titles="['衣服', '鞋子', '裤子']" @btnIndex="changePage">
       </tab-control>
       <div class="page-content" >
            {{pages[currentIndex]}}
       </div>
    </div>
</template>


<script>
import TabControl from "./TabControl.vue"
export default {
    components:{
        TabControl
    },
    data(){
        return{
            pages: ["cloths", "shoes", "pants"],
            currentIndex: 0
        }
    },
    methods:{
        changePage(index){
            this.currentIndex = index
            console.log(this.currentIndex);
        }
    }
    
}
</script>


<style scoped>

</style>

总结

  1. 父传子:父组件把数据放在动态绑定的attribute里,子组件在props里接收,props对象内声明类型和默认值
  2. 子传父:$emit发送事件和传送的数据,在父组件里监听被发送事件,被发送事件绑定对应的methods,并将数据传入其中,监听到事件发生,执行方法