跟我学微信小程序之二(组件篇)

2020-12-26  本文已影响0人  乱七八糟谈技术

上篇文章介绍了微信小程序的基本使用,如果看完了我列出的所有教程,我相信应该能熟练的玩转一个小程序了,如果遇到一些新的功能,可以去微信的官网上去查找相应功能或者控件的使用,应该都不是很难的事情。这篇文章在此基础上,再介绍一些进阶知识以及在小程序开发中我遇到的一些坑。主要内容包括如下:

这些内容是一些进阶知识,主要是尽量减少代码的复制和粘贴,比如把一些相同的组件进行封装在不同的页面进行调用,减少页面的重复代码开发。把一些相同的数据和方法进行封装,减少不同组件间拷贝相同的代码。把组件间共享的数据状态抽象出来进行统一管理,减少数据的重复。我将以一个小程序中出现率最高的列表导航功能为实例,详细的介绍如何保证这些组件之间有效的进行数据交互。

实例原型

页面导航功能是在小程序中出现频率最高的组件,以一个实际的例子为原型(请忽略界面的外观),需要实现的是俱乐部有关的活动和俱乐部的介绍。
1)页面导航组件的开发
2)俱乐部活动会在很多的页面出现,会出现在俱乐部首页,也会出现在某一个俱乐部的首页上,因此需要设计成组件,在多个页面间重用。
3)点击俱乐部介绍,会进入到俱乐部介绍的详细信息,因为在第一个页面已经获取到了俱乐部的详细信息,在俱乐部介绍详情页就不需要再次获取数据,减少网络请求次数,因此两个页面之间需要进行数据交互。
4)关注和取消关注着两个功能在很多页面出现,在每个页面也有相同的behavior,因此可以使用behavior来减少代码的重复。


Screen Shot 2020-12-25 at 3.10.35 PM.png

开发自定义组件

创建自定义组件,需要以下几步:

{

    "component": true

}
/**
   * 组件的属性列表
   */
  properties: {
    title: {
      type: String,
      value: '近期活动'
    },
    baseUrl:{
      type:String,
      value:''
    }
  },
/**
   * 组件的初始数据
   */
  data: {
    activities: []
  },
loadData(data) {
      console.log(data)
      if (data && data.length) {
        this.setData({
          activities: data
        })
      }
    },
<!--components/activityList/activityList.wxml-->
<view class="page">
  <view class="page__bd">
    <view>
      <text class="text-prompt" bindtap=”_tap“>{{title}}</text>
    </view>
    <view>
      <view wx:if="{{activities.length > 0}}" class="weui-cells weui-cells_after-title">
        <block wx:for-items="{{activities}}" wx:for-item="item" wx:key="_id">
          <navigator url ="{{baseUrl}}clubActivity/clubActivity?id={{item._id}}&needRegister={{item.activityItems.needRegister}}&needCheckIn={{item.activityItems.needCheckIn}}&activityId={{item.activityItems._id}}" class="weui-cell weui-cell_access" hover-class="weui-cell_active">
            <view class="weui-cell__hd">
              <image src="{{item.thumbnail}}" class="activity-image">
              </image>
              <mp-badge wx:if="{{item.activityItems.isTop}}" content="置顶" class="top-text" />
            </view>
            <view class="weui-cell__bd activity-list-item">
              <view class="activity-title">
                <text>{{item.title}}</text>
              </view>
              <view class="activity-subTitle">
                <view><text>时间:{{item.activityItems.occurence}}</text></view>
                <view><text>地点:{{item.activityItems.address}}</text></view>
              </view>
            </view>
            <view class="weui-cell__ft weui-cell__ft_in-access"></view>
          </navigator>
        </block>
      </view>
    </view>
  </view>
</view>

使用自定义组件

{
  "usingComponents": {
    "activityList": "../../components/activityList/activityList"
  }
}

注意:
必须要使用双引号,单引号会报错。
组件名字在这里也可以定义,不需要与文件名一致

 <view>
        <activityList id="activityList" title="近期活动" baseUrl="../../pages/club/"></activityList>
</view>

页面和组件之间交互

页面和组件之间肯定需要进行交互,它们之间的交互包括页面向子组件传递数据和调用子组件的方法。子组件向页面传递数据两个部分,方式也不同。

1) 页面访问子组件

页面访问子组件,可以调用组件的方法,可以通过方法里传递数据。也可以使用子组件里定义的properties来设置值,但properties不能多次动态设置,使用方法可以来操作子组件的数据。比如,我的实例中,导航列表里的数据是通过page传递的,page获取到数据后,通过调用子组件的loadData方法来设置子组件的data数据,进而渲染自组件的导航列表。

 if(!this.activityList)
   this.activityList = this.selectComponent("#activityList");

其中,activitylist是在wxml中定义的id,有点类似于javascript中获取dom对象。但也有可能出现获取对象为null的情况。

注意:

 this.activityList.loadData(res.result);

子组件里有相应的loadData方法即可

    loadData(data) {
      if (data && data.length) {
        this.setData({
          activities: data
        })
      }
    },

activities是组件定义的data对象,这样就可以通过父组件调用子组件的方法,进而设置子组件的data来渲染子组件。

2)子组件访问页面

子组件也需要来访问页面,这时通过获取页面对象的方式肯定是不行的。小程序提供了事件的机制来进行通讯。通过在子组件里触发事件,在页面来监听此事件达到交互的目的。

Component({
  options: {
    addGlobalClass: true // 组件外部定义的class可以影响到组件里面样式
  },

  /**
   * 组件的属性列表
   */
  properties: {
    title: {
      type: String,
      value: '近期活动'
    },
    baseUrl:{
      type:String,
      value:''
    }
  },

  /**
   * 组件的初始数据
   */
  data: {
    activities: []
  },

  /**
   * 组件的方法列表
   */
  methods: {
 /*
     * 内部私有方法建议以下划线开头
     * triggerEvent 用于触发事件
     */
    _tapEvent(){
      //触发取消回调
      this.triggerEvent("tapEvent")
    }
  }
})

上面是一个为了子组件向父组件发送事件的一个虚拟例子,当导航列表的标题被点击时,向页面组件发送一个tapEvent。

<view class="page">
  <view class="page__bd">
    <view>
      <text class="text-prompt" bindtap=”_tapEvent“>{{title}}</text>
    </view>
  </view>
</view>
const app = getApp()

Page({
   //响应子组件里的事件
  _tapEvent(){
    console.log('你点击了导航标题');
  },
})

然后,在页面的子组件wxml中指定此方法。

 <view>
   <activityList id="activityList" title="近期活动" baseUrl="../../pages/club/"  bind:tapEvent="_tapEvent">
   </activityList>
</view>

这样,就实现了子组件点击标题,发送事件到页面,页面里的响应事件的方法就会被执行。在我这个例子中,是虚拟的,没有实际意义,只是为了演示这种交互方式。

插槽的使用

子组件里的某些内容可能在每个页面中不相同,这是就可以使用插槽(slot)来由页面组件传入内容到子组件,可以支持多个插槽。使用方法比较简单,我的例子里没有实际使用,我就用一个简单的例子来演示。

<view class="content">
    <view class="title-box">标题1</view>
    <slot class="contant-box" name="slotName1"></slot>
</view>
<!--卡片-->
<activityList>  // 自定义组件的名称
  <view slot="slotName1">插入组件里面的东西1</view>
  <view slot="slotName2">插入组件里面的东西2</view>
</activityList>
Component({
  options: {
    multipleSlots: true // 在组件定义时的选项中启用多slot支持
  },
})

写在最后

本来以为用一篇文章将所有的组件和数据共享的全部内容都演示完,写着发现自定义组件内容已经很多了,后面的内容再继续演示组件和页面,页面和页面之间数据共享和交互。此外,自定义组件开发除了在上面用注释的方式中列的一些注意的地方,还需要有两个点注意。

 addGlobalClass: true // 组件外部定义的class可以影响到组件里面样式
上一篇下一篇

猜你喜欢

热点阅读