2017.11月初期待已久的原生小程序自定义组件功能已经发布,此次发布版本为 1.6.3,尽管此前有模板<template>和开源 wx-parse 工具也可以一定程度上复用代码,但相比此次原生小程序推出的组件化编程,都略有不足。话不多说,通过动手写一个组件来熟悉组件化编程带来的便利。
写在前面的话:
微信官网虽然提供了许多组件,但实际应用时仍感觉稍显疲软,如消息提示框只提供了成功与加载两种状态,那么如果失败了如何提示?还有模态框只有简单的 title 和 content 属性,内部并不支持自定义 wxml,等等,作者将工作中用到的微信组件文档中没有的 UI 自定义封装了一些,有需要的可以看下:wx-ui (持续补充中)
一、开发前准备
- 目前自定义组件相关特性处于公测阶段。如果需要使用相关特性,请确认在项目选项中已勾选“预览/上传时使用新特性”;
- 小程序基础库从 1.6.3才开始支持组件化编程,在此之前先升级开发工具,选择高于1.6.3版本的基础库来体验组件化编程。
- 微信小程序官方组件文档。 建议先看一遍,在跑一遍下方的示例代码。
二、概述
1. 目标组件
常见的价格加减组件,当设置值小于最小值时,减号 - 不可点击,当设置值大于最大值时,加好不可点击,效果如下:
2. 组件化开发需要注意两点
- 页面如何往组件传值?
- 组件如何将自身值反馈给页面?
tips: 第一遍看可能有点绕,建议将代码 copy 到开发工具跑一遍,在对照着下方的解释去理解会事半功倍。
三、价格组件实例
写一个组件,在页面中使用它。
1. 书写价格组件 price
- 组件不需要在 app.json 中的 pages 属性中进行配置,因为它不是一个页面;
- 组件和普通页面一样,有 .wxml、.wxss、*js 和 *.json 四个文件;
- 组件必须要在 *.json 文件中进行申明这是一个组件,具体代码见 price.json 文件;
- 组件的 *wxml 和 *.wxss 和页面的书写没有任何区别,这里不多说;
- 组件的 *.js 文件使用了新的构造函数 Component 代替了页面构造函数 Page,用来管理属性和事件。官方文档:Component 构造器, *.js 文件是组件中最重要的文件,在页面中使用组件时再介绍它。
1)组件页面 price.wxml
price 组件的页面显示。
<view class="quantityViewStyle">
<view class="minusStyle" bindtap="minus" style="color:{{num==min?'#DADADA':white}}">-</view>
<view class="inputViewStyle">
<input class="inputStyle" value="{{num}}" type="number" bindblur="onInputBlur"/>
</view>
<view class="plusStyle" bindtap="plus" style="color:{{num==max?'#DADADA':white}}">+</view>
</view>
2)组件样式 price.wxss
price 组件的样式文件。
.quantityViewStyle {
display:flex;
border:0rpx solid #DADADA;
border-radius:6rpx;
width: 220rpx;
}
.minusStyle {
height:58rpx;
width:60rpx;
border-right:0rpx solid #DADADA;
display:flex;
justify-content:center;
align-items:center;
}
.plusStyle {
height:58rpx;
width:60rpx;
display:flex;
justify-content:center;
align-items:center;
}
.inputViewStyle {
height:58rpx;
width:100rpx;
border-right:0rpx solid #DADADA;
}
.inputStyle {
width:80rpx;
height:54rpx;
text-align:center;
font-size:26rpx;
background:white;
}
3)组件逻辑 price.js
- Component 是个新的构造器,主要关注其中 properties 和 methods 两个属性;
- 在 properties 中,如设置 num 字段,用 type 和 value 修饰,type 表示 num 是个数值,而 value 表示 num 的默认值,如果页面调用组件时没有传值,那么 num 就是 5;
- methods 属性里写组件的相关方法,主要注意
this.triggerEvent('custom', { value: num })
这行代码,这是子组件往页面传值的唯一方式。- 组件往页面传值,通常的写法是在页面调用组件时添加一个事件监听,这样只要组件中值改变时,通过这个事件就可以监听到改变的值;
- 第一个参数为页面定义的监听事件的事件类型,名称自定义。
- 事件类型:在 index.wxml 中调用组件 price 时添加了事件监听函数
bindcustom="onPageInputChange"
,其中 bind 是微信小程序绑定事件的一个前缀,后面的 custom 才是具体的事件类型; - 要注意第一个参数的值要与事件监听函数事件类型相同;
- 事件类型:在 index.wxml 中调用组件 price 时添加了事件监听函数
- 第二个参数是个对象,表示往使用该组件的页面中传的值,在 index.js 中
onPageInputChange: function (e) { // .... }
中的参数 e 就是这地方传递过去的。
Component({
properties: {
// 这里定义了innerText属性,属性值可以在组件使用时指定
num: {
type: Number,
value: 5,
},
min: {
type: Number,
value: 0,
},
max: {
type: Number,
value: 10
}
},
methods: {
// 加法
plus: function () {
// 加值小于最大值,才允许加法运算
var num = this.data.num + 1;
if (num <= this.data.max) {
this.setData({
num: num
})
this.triggerEvent('custom', { value: num })
}
},
// 减法
minus: function () {
// 减值大于最小值,才允许减法运算
var num = this.data.num - 1;
if (num >= this.data.min) {
this.setData({
num: num
})
this.triggerEvent('custom', { value: num })
}
},
// 文本框失去焦点事件,判断输入值是否为数字
onInputBlur: function (e) {
var value = e.detail.value;
if (isNaN(value)) {
// 不是数字,直接置为最小值
this.setData({num: this.data.min})
} else {
// 是数字,输入值大于最大值,置为最大值,同理最小值
if (value > this.data.max) {
this.setData({ num: this.data.max })
} else if (value < this.data.min) {
this.setData({ num: this.data.min })
}
}
}
}
})
4)组件配置 price.json
声明 price 是个组件。
{
"component": true
}
2. 在页面中使用组件
- 在页面中使用组件,需要先在 *.json 文件中进行配置,格式如下;
-
component-tag-name
为组件在页面(*.wxml)中使用的名称,这里可以自定义,可以叫 price,也可以叫 my-price,但不可有数字,否则会报错; -
path/to/the/custom/component
为组件的路径,这里写相对路径,也就是 ../ 开头的路径,不要有后缀,具体见下方 index.json 文件。
-
{
"usingComponents": {
"component-tag-name": "path/to/the/custom/component"
}
}
- 页面需要在 app.json 中配置,别忘记了。
1)页面 index.wxml
<view style="display: flex;flex-direction: row;justify-content: space-between;padding: 0 20rpx;">
<view>¥26.3</view>
<!-- 以下是对一个自定义组件的引用 price -->
<price num="{{price}}" bindcustom="onPageInputChange"/>
<view>{{price}}</view>
</view>
- 这里调用组件的代码为
<price num="{{price}}" bindcustom="onPageInputChange"/>
; -
num
字段为设置价格组件的默认值,在 price.js 中可以在 properties 属性中找到它; -
bindcustom="onPageInputChange"
为自定义事件监听函数,用法为监听价格组件文本框中数值变化。在下方的 index.js 中可以看到事件处理函数 onPageInputChange 有个参数 e,这个参数需要在组件中进行传递。通过 e.detail.value 可以获取文本框中的值。
2)页面逻辑 index.js
Page({
data: {
price: 5
},
onPageInputChange: function (e) {
this.setData({ price: e.detail.value})
}
})
3)配置文件 index.json
声明使用组件 price。
{
"usingComponents": {
"price": "../price/price"
}
}
4)app.json
配置 index 页面。
{
"pages":[
"pages/index/index"
],
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle":"black"
}
}