前言

对于组件的封装,在小程序当中对于多个页面的复用有着重要的作用,小程序中注册的每个页面都是独立的

页面的显示view层与逻辑层是通过data进行绑定关联,若需要更改页面中的数据,则通过setData的方式进行修改

那么在小程序中如何自定义组件,以及自定义组件之间是如何进行通信呢

实例效果

通过上面一个简单的数字加减输入框组件,阅读完本文后,您将收获到

  1. 在小程序中如何自定义组件
  2. 在小程序页面中如何使用自定义组件
  3. 父(外部)组件如何向子组件传值
  4. 子组件如何接受父组件传递过来的值,同时渲染组件
  5. 子组件内如何进行事件交互,如何向父组件传递数据,影响父组件定义的数据
  6. 另一种方法父组件获取子组件的数据(非triggerEvent方式,即selectComponent)
  7. 达到某些条件时,如何禁止view的bindtap事件
  8. 数字加减输入框代码的优化

为什么要自定义组件?

每个小程序页面都可以看成一个自定义组件,当多个页面出现重复的结构时,可以把相同的部分给抽取出来封装成一个公共的组件,不同的部分,在页面中通过传参的方式传入组件,渲染出来即可,达到复用的目的

下面以一个简单的数字加减输入框组件为例,麻雀虽小,但五脏俱全。

怎么使用自定义组件?

在miniprogram下的目录下创建一个components文件夹,与pages目录同级,这个文件夹专门是用来放自定义组件的

例如:在components目录下创建了一个count文件夹,然后在新建Component,组件名称命名为count,微信开发者工具会自动的创建count组件

如下是 css 代码

/* components/count/count.wxss */
.count-container {width: 200rpx;display: flex;justify-content: center;border: 1px solid #ccc;margin: 30px auto;
}.count-container view {width: 30px;text-align: center;
}.count-container view:nth-child(1) {border-right: 1px solid #ccc;
}
.count-container view:nth-child(3) {border-left: 1px solid #ccc;
}.count-container input {flex: 1;text-align: center;
}.btn-disabled {background: #eee;pointer-events: none; /*微信小程序view禁掉bindtap事件,阻止点击,它是css3的一个属性,指定在什么情况下元素可以成为鼠标事件的target(包括鼠标的样式)*/
}

如下是 js 逻辑代码

// components/count/count.js
Component({/*** 组件的属性列表*/properties: {count: Number,},/*** 组件的初始数据*/data: {},/*** 组件的方法列表*/methods: {reduce() {console.log('减');var count = this.data.count - 1;if (count < 1) {count = 1;}this.setData({count,});this.triggerEvent('changeCount', count);console.log(this.data.count);},add() {console.log('加');var count = this.data.count + 1;this.setData({count,});this.triggerEvent('changeCount', count);console.log(this.data.count);},handleInput(event) {console.log(event);this.setData({count: event.detail.value,});this.triggerEvent('changeCount', event.detail.value);},},
});

自定义组件定义好了,那么如何使用呢

在pages目录下,这里我创建了一个customComponents页面

在要使用页面对应的customComponents.json中的usingComponents自定义组件的名称,同时引入组件的路径

{"usingComponents": {"count":"/components/count/count"}
}

注意

引入组件:使用相对路径地止也是可以的,如上面引入根路径/也可以,自定义组件名称区分大小写,为了代码的可读性,建议统一小写,多个字母之间用-连字符,例如:count-number

前面是自定义组件的名称,后面是声明创建该组件的路径

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

那么在对应页面(这里是customComponents),的父组件(外部)wxml中直接调用组件,以标签形式插入就可以了的

你可以将自定义组件看作为自定义的标签,对原生wxml中的view的一种拓展,在自定义组件上可以添加自定义属性,绑定自定义事件.

而在customComponents自定义页面中的逻辑代码中,如下所示

// pages/customComponents/customComponents.js
Page({/*** 页面的初始数据*/data: {countNum: 1,},/*** 生命周期函数--监听页面加载*/onLoad: function(options) {},// 父组件中自定义绑定的事件handleCount(event) {this.setData({countNum: event.detail,});},
});

在微信小程序中,使用组件就是这么简单,想要在哪个页面使用,就在哪个页面的xxx.json中声明组件,就可以了的

上面的代码也许看得有点懵逼,下面将逐步拆解的.

小程序中组件的通信与事件

在小程序中,组件间的基本通信方式有以下几种

  1. wxml数据绑定:用于父组件向子组件指定属性设置数据(以后会单独做一小节的,本篇不涉及) 事件:
  2. 用于子组件向父组件传递数据,可以传递任意数据(监听事件是组件通信的主要方式之一,自定义组件可以触发任意的事件,引用组件的页面可以监听这些事件,监听自定义组件事件的方法与监听基础组件事件的方法完全一致)
  3. 如果上面两种方式都无法满足,在父组件中还可以通过this.selectComponent(“类名或ID”)方法获取子组件的实例对象,这样在父组件中不必通过event.detail的方式获取,可以直接访问子组件任意的数据和方法(后面也会提到)

如何向自定义组件内传递数据?

在页面customComponents的wxml中,以标签的方式,引用count组件

这个页面,可以视作为父组件,父组件中可以定义当前组件的数据,方法等

定义在父组件中的数据,也可以视作为外部数据,例如:上面的countNum就是挂载在customComponents中的data下的,初始值countNum等于 1

父(外部)组件向子(内)组件传递数据是通过在子组件上自定义属性的方式实现的,自定义属性可以是基本数据类型(数字Number,字符串String,布尔(Boolean)等)与复杂数据类型(对象Object,数组Array)

如本示例中的,count组件上定义了count属性,这个名字并不是固定的,和自定义了changeCount方法

也就是,将countNum变量对象赋值给count属性,给count组件自定义了changeCount方法

注意

handleCount方法是定义在父组件当中的

// 父组件中自定义绑定的事件handleCount(event){this.setData({countNum: event.detail  // 通过event.detail可以拿到子组件传过来的值,如果不重新设置countNum,父组件的countNum是不会更新同步的})}

子组件内如何接收父组件传递过来的值?

在子组件内,Component构造器可以用于定义组件,调用Component构造器时,可以指定组件的属性,数据,方法等

其中properties对象接收外部(父)组件传过来的自定义属性数据,可以是对象,数组,基本数据类型等

而data是定义当前组件内的私有变量数据,可用于组件模板的渲染

至于变量数据对象是定义在 properties 下还是挂载在 data 下,具体要看组件的使用

凡是外部传递过来的数据,那么就放置在properties中,而若是当前(内部)的组件模板渲染,那么就挂载在data下

而这个data下面挂载的数据,又分为普通数据字段,和纯数据字段,其中后者纯数据字段变量用_开头

这些指定的纯数据字段需要在Component构造器的options对象中指定pureDataPattern的一个正则表达式,字段名符合这个正则表达式的字段将成为纯数据字段

在小程序组件中,某些情况下,一些data中的字段,也包括setData中设置的字段,有些只参与业务逻辑,不会展示在界面上,也不会传递给其他组件,仅仅在当前组件内部使用

这样的数据字段被称为纯数据字段,它可以定义在全局作用域中,也可以定义在data下,若定义在data下,它会被记录在this.data中,而不会参与任何界面的渲染过程

如下所示

Component({options: {pureDataPattern: /^_/, // 指定所有 _ 开头的数据字段为纯数据字段},data: {a: true, // 普通数据字段_b: true, // 纯数据字段},methods: {myMethod() {this.data._b; // 纯数据字段可以在 this.data 中获取this.setData({c: true, // 普通数据字段_d: true, // 纯数据字段});},},
});

上面的组件中的纯数据字段不会被应用到wxml中

<view wx:if="{{a}}"> 这行会被展示 </view>
<view wx:if="{{_b}}"> 这行不会被展示 </view>

在properties对象中接收外部组件传递过来的数据

// components/count/count.js
Component({/*** 组件的属性列表*/properties: {count: Number, // 在这里接收外部组件传递过来的属性,同时确定传递过来数据的类型,类型有String,Boolean,Object,Array等},/*** 组件的初始数据*/data: {},/*** 组件的方法列表*/methods: {},
});

那么在内部组件中如何渲染呢,直接将properties下的变量对象与wxml中通过{{}}插值表达式进行绑定关联就可以了的 如下所示input中的count

<view><view class="count-container"><view>-</view><input type="number" value="{{count}}" /><view>+</view></view>
</view>

以上就完成了子组件接收父组件外部传过来的值,然后在组件中渲染的过程

那么想要操作当前组件的数据,对加减输入框进行动态操作,在组件元素上绑定相应的事件操作就可以了的

<view><view class="count-container"><view bindtap="reduce" class="{{count == 1? 'btn-disabled': ''}}}">-</view><input bindinput="handleInput" type="number" value="{{count}}" /><view bindtap="add">+</view></view>
</view>

在+,-上添加了bindtap方法,进行业务逻辑的处理,如下所示

// components/count/count.js
Component({/*** 组件的属性列表*/properties: {count: Number,},/*** 组件的初始数据*/data: {},/*** 组件的方法列表*/methods: {// 减操作reduce() {console.log('减');var count = this.data.count - 1;if (count < 1) {count = 1;}this.setData({count,});},// 加操作add() {console.log('加');var count = this.data.count + 1;this.setData({count,});},// 监听表单输入handleInput(event) {console.log(event);this.setData({count: event.detail.value,});},},
});

子组件如何向父组件传递数据,影响父组件定义的数据

小程序,组件与组件之间是相互隔离,独立的,通过上面的一顿操作,数字框架的加减确实已经实现了的,但是若在外部组件中,想要获取拿到子组件中的数据,如果不通过某些手段,子组件中的数据是影响不到父组件的

因为小程序当中数据的传递是单向的,也就是父组件传递数据给子组件,是通过在组件上添加自定义属性实现的,而在子组件内部的properties中接收自定义组件的属性

如果你接触过vue,与react等框架,你会发现有惊人的相似之处,vue中是props接收,而react是this.props接收

小程序正是借鉴了它们的思想.

那父组件想要拿到子组件中的数据,换而言之,子组件又如何向父组件传递数据呢?影响到父组件中定义的初始化数据呢,该怎么办呢

父组件想要拿到子组件的数据,通过在组件上绑定自定义监听事件

监听事件

  1. 事件是视图层到逻辑层的通讯方式
  2. 可以将用户的行为反馈到逻辑层进行处理
  3. 可以绑定在组件上,当达到触发事件,就会执行逻辑层中对应的事件处理函数
  4. 事件对象可以携带额外信息,如 id, dataset,touches

事件系统是组件间通信的主要方式之一。自定义组件可以触发任意的事件,引用组件的页面可以监听这些事件,监听自定义组件事件的方法与监听基础组件事件的方法完全一致

如下所示

<!-- 当自定义组件触发“myevent”事件时,调用“onMyEvent”方法 -->
<component-tag-name bindmyevent="onMyEvent" />
<!-- 或者可以写成 -->
<component-tag-name bind:myevent="onMyEvent" />

在本文示例中如下所示,bind:changeCount=“handleCount”,就是绑定了自定义changeCount事件,这句话的含义,相当于是 在count组件上监听绑定了一个changeCount事件,当触发changeCount事件时,就会调用后面父组件中定义的handleCount方法

<countclass="count"count="{{countNum}}"bind:changeCount="handleCount"
></count>

而在父组件中,声明handleCount方法,可以通过event事件对象拿到子组件中的数据

Page({handleCount: function(event) {event.detail; // 自定义组件触发事件时提供的detail对象},
});

既然在父组件中通过监听自定义事件,那么在子组件内部如何触发该事件呢

触发事件

自定义组件触发事件时,需要使用 triggerEvent 方法,指定事件名、detail对象和事件选项

如下所示

Component({properties: {},methods: {onTap: function() {var myEventDetail = {}; // detail对象,提供给事件监听函数var myEventOption = {}; // 触发事件的选项this.triggerEvent('自定义事件名称myEvent', myEventDetail, myEventOption);},},
});

在本示例中:

// components/count/count.js
Component({/*** 组件的属性列表*/properties: {count: Number,},/*** 组件的初始数据*/data: {},/*** 组件的方法列表*/methods: {// 减reduce() {console.log('减');var count = this.data.count - 1;if (count < 1) {count = 1;}this.setData({count,});this.triggerEvent('changeCount', count);},// 加add() {console.log('加');var count = this.data.count + 1;this.setData({count,});this.triggerEvent('changeCount', count);},// 监听输入框handleInput(event) {console.log(event);this.setData({count: event.detail.value,});this.triggerEvent('changeCount', event.detail.value);},},
});

至于为什么有三次triggerEvent,每次加,减都是子组件内部的操作,外部组件想要实时获取到,那么就需要触发父组件监听的自定义方法的,同时triggerEvent方法的第二个参数代表的就是当前子组件的内部所要传递给父组件的数据

当子组件触发了changeCount方法,会调用父组件的handleCount方法,在父组件中进行重新setData父组件中的初始化数据,就可以更新同步到页面上了的

这个过程虽然有些绕,曲折,对于初学者,需要自行感悟,理一下的

这个triggerEvent,就相当于vue中的this.$emit(‘绑定在父组件自定义事件名称’,携带的数据)方法的,而在React中是通过this.props.方法接收,调用父组件的方法

注意

在父组件中监听的自定义方法(如上示例的changeCount),是通过triggerEvent进行触发的,是放置在子组件内部要监听的方法内的,而不是定义在methods方法中

changeCount() { // 这是错误的写法,有些小伙伴误以为自定义方法,就必须要写成方法这种形式的,它只是一个名称而已
}

通过以上的代码示例,文字介绍,就知道子组件如何向父组件传递数据,影响父组件定义的数据

子组件想要传递数据给父组件,影响父组件初始化定义的数据

  1. 首先需要在父组件上的自定义组件上设置监听自定义方法
  2. 在子组件内部的事件方法中,通过triggerEvent触发父组件中的自定义事件名称,同时,triggerEvent第二个参数为携带所需的数据
  3. 在父组件中定义的方法,即可通过事件对象event.detail的方式获取到子组件中传递过来的值
  4. 在父组件中,重新setData数据即可更新父组件中初始化的数据,从而渲染到页面上

以上是通过triggerEvent的方式,并携带参数传递给自定义事件,从而在父组件中可以通过event.detail的方式拿到子组件中的数据

其实,还有另外一种简便的方法,同样可以拿到

父组件通过this.selectComponent拿到子组件中的数据

前提条件

需要在父组件的引用自定义组件上,添加class或id

例如:在count组件上添加了一个class类count

<countclass="count"count="{{countNum}}"bind:changeCount="handleCount"
></count>

那么,在父组件中的handleCount中里调用 this.selectComponent,获取子组件的实例数据

调用时需要传入一个匹配选择器 selector,class与Id都可以,如,this.selectComponent(‘类或ID’)

本示例中是this.selectComponent(’.count’),如下示例代码所示

 handleCount(){console.log(this.selectComponent('.count'));var count = this.selectComponent('.count');this.setData({countNum: count.data.count  // 重新赋值setData countNum数据})}

这种方法也是可以的,在小程序当中也很常用

如何禁止掉view的bindtap事件?

在做数字加减输入框时,对于减到某个数值时,想要禁用状态,遇到类似的情况时,要么把view换成button

然后当达到某个条件时,将button的状态设置为disabled属性也是可以的

但是若不用button呢,该怎么实现呢

如果用view代替button,虽然在某个条件下,可以达到样式上是禁用状态,但是如果你在测试时,这个减操作仍然是在不断触发的

这样显然有些鸡肋

解决这个问题: 借助了 css3 中的一个非常好用的特性 在指定的类上添加一个pointer-events: none就可以了的

.btn-disabled {pointer-events: none; /*微信小程序view禁掉bindtap事件,阻止点击,它是css3的一个属性,指定在什么情况下元素可以成为鼠标事件的target(包括鼠标的样式)*/
}

这个属性,作用在view上,可以组织bindtap的点击

数字加减输入框代码的优化

在上面实现数字加减框组件,分别给减,加绑定了两个方法,多次出现了triggerEvent

<view><view class="count-container"><viewbindtap="handleCount"data-count="-1"class="{{count == 1? 'btn-disabled': ''}}}">-</view><input bindinput="handleInput" type="number" value="{{count}}" /><view bindtap="handleCount" data-count="1">+</view></view>
</view>

在上面的加减中绑定一个相同的事件方法handleCount,而通过设置data-xx属性,判断是加还是减 那么在逻辑代码中

methods: {handleCount(event){var count = event.currentTarget.dataset.count;count  = this.data.count+Number(count);  // 这里之所以要把count,转换为Number,因为自定义属性的count是字符串,+加号字符串拼接,会变成一个字符串if(count < 1) {count = 1;}this.setData({count: count})this.triggerEvent('changeCount', count);},
}

上面的代码相比于前面写的代码,就要简便得不少,看着舒服得多

在做这种类似的业务逻辑时,不妨可以通过这种方式对代码进行优化的

结语

本文主要是讲到了在小程序中父子组件之间如何进行通信,父组件向子组件传递数据是通过在引用组件上绑定自定义属性实现的

而子组件是通过在properities对象中进行接收的,子组件如何向父组件传递数据,它是通过在引用组件上绑定监听自定义事件,然后在子组件的事件方法内,通过this.triggerEvent方法进行触发自定义事件名,并可以携带子组件内的数据,在父组件中的函数中

可以通过event.detail可以拿到子组件中传递给父组件的值,从而重新在setData数据,就可以更新父组件中的初始化数据

这个关系虽然有点绕,至于重要性不言而喻.

小程序-实现自定义组件以及自定义组件间的通信相关推荐

  1. 微信小程序开发学习5(自定义组件)

    微信小程序开发学习5(自定义组件) 1.学习目标 能够知道如何自定义小程序组件 能够知道小程序组件中behaviors的作用 能够知道如何安装和配置vant-weapp组件库 能够知道如何使用MobX ...

  2. 微信小程序网悦新闻开发--自定义组件开发(六)

    目录 微信小程序网悦新闻开发--功能介绍(一) 微信小程序网悦新闻开发--小程序配置(二) 微信小程序网悦新闻开发--首页模块开发(三) 微信小程序网悦新闻开发--视频模块开发(四) 微信小程序网悦新 ...

  3. 微信小程序template模板与component自定义组件的区别和使用

    前言: 除了component,微信小程序中还有另一种组件化你的方式template模板,这两者之间的区别是,template主要是展示,方法则需要在调用的页面中定义.而component组件则有自己 ...

  4. 小程序内配置公众号关注组件

    official-account 基础库 2.3.0 开始支持,低版本需做兼容处理. 用户扫码打开小程序时,开发者可在小程序内配置公众号关注组件,方便用户快捷关注公众号,可嵌套在原生组件内. Tips ...

  5. 【微信小程序经验】各类图表相关组件+Demo源码(折线图,柱状图,K线,分时图)

    各类图表功能,小程序自带API并没有提供,所以很多人就用了其他方法来实现,我将这些实现方法和教程聚合一下,以便能够迅速而方便的使用: 相关文章: 在微信小程序中绘制图表(part1) 在微信小程序中绘 ...

  6. 后台版本趣味测试威信小程序源码下载支持自定义问题等等

    这是一款有后台版本的趣味测试小程序 支持用户自定义添加和删除问题 支持用户个人中心等等 该程序是微擎框架的,所以后台需要有微擎 PS:该小程序的登录接口并未替换更新还是采用的旧版登录接口所以登录只会显 ...

  7. 小程序源码:后台版本趣味测试微信小程序源码下载支持自定义问题等等

    这是一款有后台版本的趣味测试小程序 支持用户自定义添加和删除问题 支持流量主后台设置 支持用户个人中心等等 该程序是微擎框架的,所以后台需要有微擎 PS:该小程序的登录接口并未替换更新还是采用的旧版登 ...

  8. 独立后台月老办事处一元交友盲盒微信小程序源码下载,可自定义价格

    最近超火的月老办事处交友盲盒程序,支持自定义存取价格,独立后台,存取小纸条盲盒交友匹配交友趣味交友同城交友 前端时间小编也给大家发过一款云开发版本的月老小程序 今天呢给大家发的这一款是独立后台的需要服 ...

  9. uni-app实现小程序沉浸式导航栏/顶部组件占满导航栏

    uni-app实现小程序沉浸式导航栏/顶部组件占满导航栏 如果你只是想单纯的让屏幕最上方的那个组件(比如swiper)将通知栏(导航栏)填充满,那么你只需要在page.json文件中加入以下代码到你想 ...

  10. 微信小程序 怎么插入图片?image组件的使用教程。

    这期我们来学学怎么在小程序中插入图片 1.image组件的简介 是小程序中一个图片的组件 image组件有一个默认宽度和高度(宽300px,高240px) 支持 JPG.PNG.SVG.WEBP.GI ...

最新文章

  1. Keras图数据学习
  2. jQuery图片自动添加水印插件
  3. 让用户故事真的像故事那样
  4. BZOJ 4706: B君的多边形 找规律
  5. ABAP web service schema node的处理
  6. java压缩成.tar_java压缩tar.gz | 学步园
  7. linux实例 批量修改图片文件名
  8. 循环首次适应算法_数据结构与算法之2——排序问题
  9. Mysql学习总结(68)——MYSQL统计每天、每周、每月、每年数据 SQL 总结
  10. 【转】The C10K problem(翻译 中文版)
  11. ftp-cmd常用命令
  12. MySQL 误删数据恢复
  13. HTML-CSS前端大作业一站式教育机构响应式前端网页网站模板
  14. 利用outlook邮箱申请teams账号
  15. 超美二次元响应式引导页源码
  16. Cadence OrCAD 原理图栅格样式的大小设置方法图文教程及视频演示
  17. GitHub牛逼开源项目!像写 Markdown 一样画流程图
  18. Unity游戏设计之飞碟游戏
  19. elementUi基础入门学习
  20. 创业时全力以赴和留有后路的结果相同吗?

热门文章

  1. 计算机视觉与互动投影
  2. 如何定时执行BAT(批处理)、VBS、Python等脚本 —— 定时执行专家
  3. MySQL查询本年数据
  4. (Frp第一篇)Frp内网穿透安装教程#Frps服务端一键安装脚本#
  5. mysql general error,mysql General error: 1267 Illegal mix of collations错误​
  6. 摩拜单车已全面接入美团APP 未来将更名为美团单车
  7. Java中public等修饰符
  8. 红日安全ATT&CK靶机实战系列之vulnstack2
  9. c++的学习——晶晶赴约会
  10. BUG Free的简易安装