微信小程序——component自定义组件的使用

最近跟着微信小程序的教学视频及官方文档,开始入门微信小程序的开发。

在刚开始学习的时候,就明显感受到了微信小程序的开发与Web开发的相似度非常高,由于本人学过前端开发的一些知识,所以学习小程序的开发过程中进度比较快,也几乎系统地学完了微信小程序的原生框架。学习过程中,发现对自定义组件的:父组件与子组件之间数据的传递方法理解不深刻,因此写下此篇文章来梳理一下知识体系。

下图为本人粗糙整理的思维图:

首先先看一下需要实现的需求:

需求很简单,就是模拟微信tabber的功能,实现点击导航栏之后跳转到相应的页面。

需要注意的是,因为导航栏都是几个页面所共有的组件,因此我们需要把导航栏单独抽离出来,封装成自定义组件,方便后面的使用与维护。下面开始敲代码吧!

第一步:新建一个component文件夹,在里面创建一个自定义组件

并在cpt1.wxml中搭好组件框架:

扫描二维码关注公众号,回复: 10571226 查看本文章
<view class="cpt1">
    <view class="title_box">
        <view class="title_item">首页</view>
        <view class="title_item">关注</view>
        <view class="title_item">社区</view>
        <view class="title_item">个人</view>
    </view>
    <view class="content_box">内容</view>
</view>

第二步:在index1.json文件中引用自定义组件,并在index1.wxml中使用自定义组件

index1.json文件配置如下:

{
  "usingComponents": {
    "cpt1": "../../component/cpt1/cpt1"
  }
}

index1.wxml文件内容如下:

<cpt1></cpt1>

到此之后,我们先查看一下效果如何:

我们可以看到,已经成功通过自定义组件的使用,把内容显示到了我们的主页当中。

第三步:设置导航栏样式

.title_box{
    display: flex;
    padding: 20rpx;
}

.title_item{
    flex: 1;
    display: flex;
    justify-content: center;   /*文字水平居中,调整使全行排满*/
    text-align: center;
}

.active{
    color: red;
    border-bottom: 2px red solid;
}

效果如下:

已经有点样子了,不过细心的读者可能会发现,我们在导航栏样式中设置了active样式,却没有在wxml文件中使用,那是因为我们要根据用户点击的操作来判断当前活跃的导航入口,最后决定给予哪一个导航入口active的属性。

我们使用自定义组件的原因是为了解耦,让我们的程序代码不至于太过臃肿,但是现在自定义组件的导航栏内容是直接写死了的,万一其他的地方需要用到不同内容的导航栏时,又需要重新创建一个自定义组件,显得有点麻烦,能偷懒就尽量偷懒吧,所以便有了第四步。

第四步:通过主组件(调用者)向子组件(被调用者)传参的方式,传入导航栏内容到自定义组件当中。首先需要在自定义组件的js文件当中配置如下:

properties: {
    list:{
      type: Array,
      value: []
    }
  },

用于接收来自主组件传入的参数

然后在主组件的js文件当中配置:

data: {
    list:[
      {
        id: 1,
        name: "首页",
        isActive: true
      },
      {
        id: 2,
        name: "关注",
        isActive: false
      },
      {
        id: 3,
        name: "社区",
        isActive: false
      },
      {
        id: 4,
        name: "个人",
        isActive: false
      }
    ]
  },

初始化主组件当中的内容

最后在主组件的wxml文件当中把主组件的数据传入到子组件当中:

<cpt1 list="{{list}}"></cpt1>

第五步:在子组件的wxml文件中渲染接收到的数据

<view class="cpt1">
    <view class="title_box">
        <view
         class="title_item {{item.isActive?'active':''}}"
         wx:for="{{list}}"
         >{{item.name}}</view>
    </view>
    <view class="content_box">内容</view>
</view>

效果图:

第六步:实现单击导航栏切换。在自定义组件的js文件中添加方法,改变isActive状态(这里需要注意的是:在普通的页面中,编写的方法需与data同一列,而在自定义组件中编写的方法需要写在methods当中)。

methods: {
    clickActive:function(e){
      const index = e.currentTarget.dataset.operation; // 得到用户点击的导航序列号
      let {list} = this.data;
      list.forEach((v, i)=>i+1===index?v.isActive=true:v.isActive=false); // 遍历,改变状态
      this.setData({   // 重新设置回去
        list
      })
    }
  }

然后自定义组件的wxml文件当中把点击的导航栏序列号传入事件方法中:

<view class="cpt1">
    <view class="title_box">
        <view
         class="title_item {{item.isActive?'active':''}}"
         wx:for="{{list}}"
         bindtap="clickActive"    /*绑定事件方法*/
         data-operation="{{item.id}}"   /*把点击的序列号传入事件方法*/
         >{{item.name}}</view>
    </view>
    <view class="content_box">内容</view>
</view>

最终实现了点击切换效果,不过此时只是导航栏的切换,而下面的内容却也是我们最开始就写死了的,不符合正常情况,因此我们趁热打铁,继续完善一下。

第七步:主组件使用子组件的同时,传入需显示的内容到子组件当中。

我们把子组件中显示的“内容”删除,换为:

<view class="content_box">内容</view>
换为:
<view class="content_box">
        <!--内容卡槽,谁用组件,则插入内容-->
        <slot></slot> 
    </view>

主组件中使用子组件的同时,传入内容:

<cpt1 list="{{list}}">
    <!--调用组件的同时,把特定内容插入组件内容卡槽-->
    <view wx:if="{{list[0].isActive}}">1</view>
    <view wx:if="{{list[1].isActive}}">2</view>
    <view wx:if="{{list[2].isActive}}">3</view>
    <view wx:if="{{list[3].isActive}}">4</view>
</cpt1>

不过并没有如我们所愿,点击任意一个导航,内容始终显示的是“1”。排查问题之后,会发现:

原来是因为我们根据用户点击的导航判断导航序列号,改变的导航活跃状态只应用在了子组件当中,而主组件当中的数据始终是我们最开始初始化的一组:

 /**
   * 页面的初始数据
   */
  data: {
    list:[
      {
        id: 1,
        name: "首页",
        isActive: true
      },
      {
        id: 2,
        name: "关注",
        isActive: false
      },
      {
        id: 3,
        name: "社区",
        isActive: false
      },
      {
        id: 4,
        name: "个人",
        isActive: false
      }
    ]

解决方法就是通过子组件向主组件传参、通过回调的方式来同步改变主组件当中数据的状态。

第八步:在子组件的js文件中的方法里配置为:

methods: {
    clickActive:function(e){
      const index = e.currentTarget.dataset.operation;
      let {list} = this.data;
      list.forEach((v, i)=>i+1===index?v.isActive=true:v.isActive=false);
      this.triggerEvent("changeList", {list})  // 回调主组件的方法,同步数据的改变
      this.setData({
        list
      })
    }
  }

然后在主组件中配置如下:

<cpt1 list="{{list}}" bindchangeList="changeList"> /*绑定回调方法,bind加上子组件回调的方法名*/ 
    <!--调用组件的同时,把特定内容插入组件内容卡槽-->
    <view wx:if="{{list[0].isActive}}">1</view>
    <view wx:if="{{list[1].isActive}}">2</view>
    <view wx:if="{{list[2].isActive}}">3</view>
    <view wx:if="{{list[3].isActive}}">4</view>
</cpt1>

最后在主组件的js文件中创建回调的方法:

// 根据子组件的参数值改变list数据
  changeList(e){
    this.setData({
      list: e.detail.list
    })
  }

终于实现了预期的效果。

发布了14 篇原创文章 · 获赞 1 · 访问量 593

猜你喜欢

转载自blog.csdn.net/qq_35149975/article/details/103997039