12 | 自定义组件小实验

1、创建自定义组件

首先,创建一个位于 /components/Tabs 下的组件Tabs
在这里插入图片描述

2、自定义组件的内容、样式和行为

tabs.wxml

<view class="tabs">
  <view class="tabs_title">
    <view
     wx:for="{{tabs}}"
     wx:key="id"
     class="title_item {{item.isActive?'active':''}}"
    >
      {{item.name}}
    </view>
  </view>
  <view class="tabs_content">
    内容
  </view>
</view>

tabs.js

// components/Tabs/Tabs.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
  },
  /**
   * 组件的初始数据
   */
  data: {
    tabs:[{
      id: 0,
      name:"首页",
      isActive:true
    },
    {
      id: 1,
      name:"原创",
      isActive:false
    },
    {
      id: 2,
      name:"分类",
      isActive:false
    },
    {
      id: 3,
      name:"关于",
      isActive:false
    }]
  },
  /**
   * 组件的方法列表
   */
  methods: {
  }
})

tabs.wxss

.tabs{}
.tabs_title{
  display: flex;
  padding: 10rpx 0;
}
.title_item{
  display: flex;
  /* flex增长系数 */
  flex: 1;
  /* 水平对齐 */
  justify-content: center;
  /* 垂直对齐 */
  align-items: center;
}
.active{
  color: red;
  border-bottom: 10rpx solid currentColor;
}
.tabs_content{}

3、声明引入自定义组件

新建一个页面,在 *.json文件 中引入这个自定义组件
demo6.json

{
  "usingComponents": {
    "tabs":"/components/Tabs/Tabs"
  }
}

demo6.wxml

<tabs></tabs>

在这里插入图片描述

4、加入标题激活选中效果

tabs.wxml
增加了 bindtap 和 data-index两个属性

<view class="tabs">
  <view class="tabs_title">
    <view
     wx:for="{{tabs}}"
     wx:key="id"
     class="title_item {{item.isActive?'active':''}}"
     bindtap="hanldeItemTap"
     data-index="{{index}}"
    >
      {{item.name}}
    </view>
  </view>
  <view class="tabs_content">
    内容
  </view>
</view>

tabs.js

// components/Tabs/Tabs.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
  },
  /**
   * 组件的初始数据
   */
  data: {
    tabs:[{
      id: 0,
      name:"首页",
      isActive:true
    },
    {
      id: 1,
      name:"原创",
      isActive:false
    },
    {
      id: 2,
      name:"分类",
      isActive:false
    },
    {
      id: 3,
      name:"关于",
      isActive:false
    }]
  },
  /**
   * 组件的方法列表
   */
  methods: {
    hanldeItemTap(e){
    /* 1 绑定点击事件  需要在methods中绑定
      2 获取被点击的索引 
      3 获取原数组 
      4 对数组循环
        1 给每一个循环性 选中属性 改为 false
        2 给 当前的索引的 项 添加激活选中效果就可以了!!!
       
       5 点击事件触发的时候 
          触发父组件中的自定义事件 同时传递数据给  父组件  
          this.triggerEvent("父组件自定义事件的名称",要传递的参数) */
    // 2 获取被点击的索引
    const {index} = e.currentTarget.dataset
    // 3 获取原数组 
    let {tabs} = this.data
    // 4 对数组循环
    // [].forEach 遍历数组 遍历数组的时候修改了v, 也会导致原数组被修改
    tabs.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false)
    this.setData({
      tabs
    })
    }
  }
})

疑惑?

1. const {index} 和 let {tabs} 为什么要加{ }?
这种做法叫做解构,对复杂类型进行解构的时候复制了一份,只是变量的引用而已,例如, let {tabs} = this.data 相当于 let tabs = this.data.tabs , 但是,还有一种最严谨的做法,就是重新拷贝一份数组,再对这个数组的备份进行处理,如 let tabs=JSON.parse(JSON.stringify(this.data.tabs));

2. const和let有什么区别?

let声明的变量可以改变,值和类型都可以改变,没有限制。
const声明的变量不得改变值,这意味着,const一旦声明变量,就必须立即初始化,不能留到以后赋值。
参考的文章
在这里插入图片描述

5、使用父向子传递数据

我们现在来修改一下自定义组件(子组件)中内部的数据(tabs),将这个数据抽离出来变成页面(父组件)的数据,然后将这个数据传递过来,即由父组件将这个数据传递给子组件。

那么,为什么要这么做呢?
假想一下,如果我们将这些标题数据写死在这个自定义组件中,万一有其他页面也要使用这个自定义组件,那么其他页面怎么实现它的动态化呢? 所以啊,我们得把这些数据抽离出来,由页面(父组件)将数据传给自定义组件(子组件),子组件再进行接收并调用

步骤:

(1)先在父组件(页面)的js文件中,在data项中写入上面的tabs数据

// pages/demo6/demo6.js
Page({
  /**
   * 页面的初始数据
   */
  data: {
    tabs:[{
      id: 0,
      name:"首页",
      isActive:true
    },
    {
      id: 1,
      name:"原创",
      isActive:false
    },
    {
      id: 2,
      name:"分类",
      isActive:false
    },
    {
      id: 3,
      name:"关于",
      isActive:false
    }]
  }
})

(2)父组件(页面)向子组件传递数据时,需要通过标签属性的方式来传递
在这里插入图片描述

(3)在子组件的js文件中进行接收

// components/Tabs/Tabs.js
Component({
  /**
   * 里面存放的是 要从父组件中接收的数据
   */
  properties: {
    // 要接收的数据的名称
    tabs:{
      // type  要接收的数据的类型 
      type:Array,
      // value  默认值
      value:[]
    }
  },
//和上面的一样
. . .
}

(4)在子组件的wxml文件中将这个数据当成是data中的数据直接用即可
在这里插入图片描述
最后的效果还是一样
在这里插入图片描述

6、使用子向父传递数据

为什么要使用子向父传递数据呢?上面不是已经完美了吗?
然而并不是,你有没有发现以下问题:

  1. data中没有数据,为什么使用 let {tabs} = this.data还能成功?
    解答看下图
    在这里插入图片描述
  2. 看开发工具的控制台,为什么原创的isActice还是false?
    在这里插入图片描述
    解答
    在这里插入图片描述
    也就是点击原创时会将tabs数据再拷贝一份到data项中,这也就使得data中有两个tabs数据(首页一个,点击原创又一个),其实这里我也说不懂,有没有大佬发表见解 。。。

所以接下来,我们需使用子向父传递数据的索引,解决这一问题,步骤:

(1)先在父组件中自定义触发事件

<tabs tabs="{{tabs}}" binditemChange="handleItemChange">
</tabs>

(2)在子组件的js文件中绑定父组件自定义的触发事件,传递tabs的索引

methods: {
    hanldeItemTap(e){
    /* 1 绑定点击事件  需要在methods中绑定
      2 获取被点击的索引 
      3 获取原数组 
      4 对数组循环
        1 给每一个循环性 选中属性 改为 false
        2 给 当前的索引的 项 添加激活选中效果就可以了!!!
       
       5 点击事件触发的时候 
          触发父组件中的自定义事件 同时传递数据给  父组件  
          this.triggerEvent("父组件自定义事件的名称",要传递的参数) */
    // 2 获取被点击的索引
    const {index} = e.currentTarget.dataset
    // 5 触发父组件中的自定义事件 同时传递数据给  父组件  
    this.triggerEvent("itemChange",{index})
    // 3 获取原数组 
    // let {tabs} = this.data
    // 4 对数组循环
    // [].forEach 遍历数组 遍历数组的时候修改了v, 也会导致原数组被修改
    // tabs.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false)
    // this.setData({
    //   tabs
    // })
    }
  }

(3)在父组件的js文件写触发事件的行为,用来接收子组件传递的数据

// 自定义事件 用来接收子组件传递的数据的
  handleItemChange(e) {
    // 接收传递过来的参数
    const { index } = e.detail;
    let {tabs} = this.data
    tabs.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false)
    this.setData({
      tabs
    })
  }

总结:当使用父向子传递数据时,子组件不能改变传递过来的数据,如上面说的isActive激活状态,只有使用子向父传递数据的方法,通过索引值(index)触发父组件的自定义事件来改变数据

7、使用插槽slot改变页面内容

你如果跟着上面做会发现,不管你点导航栏哪个选项,都只显示内容两字,所以,为了改变页面内容,我们可以使用slot标签

步骤:
(1)在子组件中定义一个slot标签

<view class="tabs">
  <view class="tabs_title">
    <view
     wx:for="{{tabs}}"
     wx:key="id"
     class="title_item {{item.isActive?'active':''}}"
     bindtap="hanldeItemTap"
     data-index="{{index}}"
    >
      {{item.name}}
    </view>
  </view>
  <view class="tabs_content">
    <slot></slot>
  </view>
</view>

(2)在父组件中使用block包装元素和wx:if控制属性来写页面内容

<tabs tabs="{{tabs}}" binditemChange="handleItemChange">
</tabs>
<block wx:if="{{tabs[0].isActive}}">
  hello
</block>
<block wx:elif="{{tabs[1].isActive}}">你好</block>
<block wx:elif="{{tabs[2].isActive}}">savadica</block>
<block wx:else>bye</block>
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值