IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> Vue--封装实现Tab -> 正文阅读

[JavaScript知识库]Vue--封装实现Tab

在学习了vue的基础知识后,尝试着做了一个Tab选项切换并对其进行封装处理,下面我们就来开始逐步实现

涉及的知识:

  • $slots:插槽
  • $parent:边界管理->?儿子通过$parent读取父级的属性和方法
  • props
  • 自定义事件:$emit

在开始该案例之前,我们先来看看其视图结构,大致如下:

<tabs :currentIndex="index" @onIndex="changeHandle">
    <tab index="1" label="导航一">内容1</tab>
    <tab index="2" label="导航二">内容2</tab>
    <tab index="3" label="导航三">内容3</tab>
</tabs>

而在浏览器里,会自动渲染成大致如下结构:

<div>
    <div>
        <ul>
            <li>导航1</li>
            <li>导航2</li>
            <li>导航3</li>
        </ul>
    </div>
    <div>
        <div>内容1</div>
        <div>内容2</div>
        <div>内容3</div>
    </div>
</div>

首先,通过 vue create xxx 命令,创建一个 vue 项目,并在 src/components 目录下新建一个名为 tabs 的文件夹,在该文件夹里分别新建 index.js、tabs.vue、tab.vue、content.vue,它们之间的关系如下:

  • 入口文件:index.js
  • 组件:tabs(tab的父组件)、tab(content的父组件)、content(tab的子组件)

组件之间的关系

tabs?->?tab:在我们封装组件中,他俩之间是通过slots进行处理的

tabs?->?content:引用关系

tab?->?content:在我们封装组件中,他俩之间是通过slots进行处理的

其次,在main.js 里引入:

import Tabs from "./components/tabs"

Vue.use(Tabs)

下面我们就直接上源码,关于相应的解释和说明都在源码里有详细的解释,因此在这里就不去过多的叙述

src/components/tab/tab.vue

<script>
export default {
    name:"Tab",
    /**
    *如何才能读到 label里面的值?
    * label 是 tab 里的一个属性,因此可以通过 props 的方式进行
    *
     */
     props: {
       label:{
         type:String,
         default:"tab"
       },
       index:{
         type:[String,Number],
         default:1
       }
     },
     computed: {
       //控制是否高亮显示
       /**
        * 返回true或者false
        * true--代表两者相等
        * false--代表两者不相等
        */
       isActive(){
         return this.$parent.currentIndex == this.index;
       }
     },
     mounted () {
      //  console.log(this.$parent.currentIndex);//1
      //this.$slots.default代表的就是tab里面的插槽,也就是内容
      // this.$parent.pans.push(this.$slots.default);
      this.$parent.pans.push(this);//将tab自己本身传给了pans,那么pans里面包含的就是完整的两个tab
     },
     methods: {
       /**
        *改变 currentIndex的值就可以改变高亮
        *但是此时问题又来了,事件是写在tab里面的,而currentIndex 是在App.vue里,
        *但是我们知道 tab属于孙子,tabs属于父亲,App属于爷爷, 于是我们可以尝试先由孙子传到父亲,再由父亲传给爷爷即可
        * 我们在tab里可以通过 this.index读取到鼠标点击时所对应的值,因此只需将其传递给tabs,再由tabs传递给App
        *这里有两种解决的方法,一是利用子传父,一级一级向上传,但是相对来说比较麻烦;而是利用事件回调,我们选用事件回调
        *
        */
       clickItemHandle(){
         //将index传递给tabs
         this.$parent.getIndex(this.index);
       }
     },
    render(){
      // console.log(this.isActive);
      let classNames = {
        tab:true,
        active:this.isActive
      };
      return (
        <li onclick={ this.clickItemHandle } class={ classNames }>{ this.label }</li>
      )
    }
}
</script>

<style scoped>

.tab {
    flex: 1;
    list-style: none;
    line-height: 40px;
    margin-right: 30px;
    position: relative;
    text-align: center;
}

.tab.active {
    border-bottom: 2px solid blue;
}

</style>

src/components/tab/tabs.vue

<script>
import Content from "./content.vue"

export default {
    name:"Tabs",
    data () {
        return {
            //存放所有内容部分的一个容器
            pans:[],      
        }
    },
    props:{
        currentIndex:{
            type:[String,Number],
            default:1
        }
    },
    components: {
        Content
    },
    methods:{
        //读取tab传递过来的index
        getIndex(index){
            // console.log(index);
            //子传父,传递给App即可
            this.$emit("onIndex",index);
        }
    },
    /**
     * 在 ul 元素里,承载的是li,也就是导航1、导航2,而导航1、导航2实际上是在 tab 里,因此,ul里面嵌套的其实就是tab
     * 我们刚刚在App.vue里引入的时候,tab是属于tabs的子元素,如果正常通过vue从tabs里面读取tab,此时读它的插槽即可,也就是通过 this.$slots.default 读到其内容
     * this.$slots.default等价于li
     */
    render(){
        return (
            <div>
                 <ul class="tabs-header">{this.$slots.default}</ul>
                 <Content pans={this.pans} />
            </div>
        )
    }
}
</script>

<style scoped>
.tabs-header {
  display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
  border-bottom: 2px solid #ededed;
}
</style>

?

src/components/tab/content.vue

<script>
export default {
    name:"Content",
    props: {
      pans:{
        type:Array,
        default:()=>{
          return []
        }
      }
    },
    render(){
      return (
        <div>
          {
            this.pans.map((ele,index)=>{
              // ele.$slots.default 读取 tab 里面的内容
              // ele.isActive ? ele.$slots.default : "" 等价于添加和删除,在不停地切换的同时即在不停的添加和删除,这是比较消耗性能的
              return <div style={{display:ele.isActive ? 'block' : 'none' }}>{ ele.$slots.default }</div>;//显示和隐藏
            })
          }
        </div>
      )
    }
}
</script>

<style>

</style>

src/components/tab/index.js

import Tabs from "./tabs.vue"
import Tab from "./tab.vue"

export default (Vue) => {
    Vue.component(Tabs.name, Tabs)
    Vue.component(Tab.name, Tab)
}

App.vue

<template>
  <div id="app">
    <!--
      这里该如何实现导航的高亮显示?
      实现的方法就是:判断当前的currentIndex的值是否等于其对应的index的值,如果相等,那么对应的index所对应的内容就高亮显示
      然后,我们可以分别在tab.vue和tabs.vue里通过props得到其对应的属性 current 和 index 
      但是此时问题就来了,current 和 index 根本就没在同一个组件里,那么该如何对两者进行对比?
      由于tabs和tab属于子父级关系,所以我们可以可通过 $parent 在tab里越级直接读取tabs的元素 
    -->
    <tabs :currentIndex="currentIndex" @onIndex="getIndexHandle">
      <tab index="1" label="中国地图">
        <div>内容1</div>
      </tab>
      <tab index="2" label="世界地图">
        <div>内容2</div>
      </tab>
    </tabs>
  </div>
</template>

<script>


export default {
  name: 'App',
  data () {
    return {
      currentIndex:1
    }
  },
  components: {
    
  },
  methods: {
    //接收tabs传递过来的index
    getIndexHandle(index){
      // console.log(index);
      this.currentIndex = index
    }
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

?

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-09-14 13:14:30  更:2021-09-14 13:16:38 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 19:31:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码