一、前言
如果小程序中有可复用的UI且具有一定的功能性,就可以使用自定义组件将其封装起来。(如果仅仅只需要复用UI可使用template)
下面介绍父子组件的数据传递方法,以及一个简单的组件和一个复杂的组件示例。
【由于刚开始写这篇文章的时候我还算是一个小程序的新手,自己看着官方文档研究并整理归纳的,有很多不足以及错误的地方。在经过一年的沉淀以后(虽然这一年我主要在写vue而不是小程序),我决定重新整理这篇浏览量比较大的文章,以免新手因我的文章走了弯路。】
二、父子组件传递数据的方法
1.父组件向子组件传递数据
parent.wxml
<my-component name="{{name}}" age="{{age}}"></my-component>
parent.js
data: {
name: 'Peggy',
age: 25
}
child.js
properties: {
name: {
type: String,
value: '小明'
},
age: Number
}
父组件向子组件传值以属性的形式,子组件以properties接收,并可指定数据类型type以及默认值value。
在wxml里可直接以{{name}}的形式使用,而在js中以this.properties.name获取。
2.子组件向父组件传值
child.js
methods: {
changeName() {
this.triggerEvent('changeName', {
name: '李四'
})
}
}
parent.wxml
<my-component name="{{name}}" age="{{age}}" bindchangeName="changeName"></my-component>
parent.js
changeName(event) {
console.log(event.detail)
// { name: '李四' }
}
子组件向父组件传递数据使用this.triggerEvent方法,这个方法接受3个参数:
this.triggerEvent('myevent', myEventDetail, myEventOption);
myevent为方法名,
myEventDetail是传到组件外的数据,
myEventOption为是否冒泡的选项,有三个参数可以设置:
bubbles 默认false 事件是否冒泡
composed 默认false 事件是否可以穿越组件边界
capturePhase 默认false 事件是否拥有捕获阶段
在父组件监听事件bindchangeName="changeName",在changeName方法里有一个event参数,可以从event.detail里拿到组件内部传出来的值。
三、简单的组件(计数器)
1. 组件功能介绍
这个组件常见于外卖软件中,用于记录想要购买的商品的数量。初始化的时候只有一个加号,点击加号以后出现数字和减号,并最后将数字传到组件外供外部使用。
2. 创建组件
首先在根目录创建components文件夹(推荐),然后创建num-controller文件夹(我取的组件名字),在这个文件夹上点击右键新建一个component,名字为index。
/components/num-controller/index.wxml
<view class="num-controller">
<image src="images/minus.png" class="{{num <= min ?'hide': ''}}" bindtap="sub"></image>
<text class="num">{{num}}</text>
<image src="images/plus.png" bindtap="add"></image>
</view>
这段代码就是加减两个按钮和一个数字。
/components/num-controller/index.json
{
"component": true,
"usingComponents": {}
}
这个文件在创建component的时候会自动写入这段代码。
/components/num-controller/index.js
Component({
/**
* 组件的属性列表
*/
properties: {
nameId: {
type: String
},
num: {
type: Number,
value: 0
},
int: {
type: Number,
value: 1
},
min: {
type: Number,
value: 0
}
},
/**
* 组件的初始数据
*/
data: {
},
/**
* 组件的方法列表
*/
methods: {
numChange() {
this.triggerEvent('numChange', {
num: this.properties.num,
nameId: this.properties.nameId
})
},
add() {
this.setData({
num: this.properties.num + this.properties.int
})
this.numChange()
},
sub() {
this.setData({
num: this.properties.num - this.properties.int
})
this.numChange()
}
}
})
在组件内部我定义了4个属性,properties是父组件传给子组件的属性。
nameId用来标识子组件的唯一性,如果在父组件内有多个计数器,子组件想把改变的数据传给父组件时可以用到;
num代表计数器中的数字,默认为0;
int代表加减一次改变多少,默认为1;
min代表计数器的最小值,等于这个值时减号会消失,默认为0。
同时在子组件内定义了两个方法:
add点击加号触发,首先改变子组件内部的数字,同时触发numChange方法将改变的数字传到组件外部;
sub点击减号触发,首先改变子组件内部的数字,同时触发numChange方法将改变的数字传到组件外部。
3. 引入组件
假如我要在index.wxml里引入组件:
<num-controller nameId="num1" num="{{num1}}" bindnumChange="numChange"></num-controller>
<num-controller nameId="num2" num="{{num2}}" int="{{5}}" min="{{5}}" bindnumChange="numChange"></num-controller>
<num-controller nameId="num3" num="{{num3}}" int="{{100}}" bindnumChange="numChange"></num-controller>
index.json
{
"usingComponents": {
"num-controller": "/components/num-controller/num-controller"
}
}
想在页面中使用组件必须在json文件里注册组件。
index.js
Page({
data: {
num1: 0,
num2: 10,
num3: 100
},
numChange(event) {
const {num, nameId} = event.detail
this.setData({
[nameId]: num
})
}
})
data里的num1, num2, num3是从组件外传入的num,在numChange方法里用event.detail可以拿到组件内部通过this.triggerEvent传出来的数据,然后根据业务需求做逻辑修改。
四、复杂的组件(筛选面板)
这是一个二级菜单,点击左边(一级)会改变右边(二级)的展示。
1. 创建组件并引入
组件内部:
/components/filter-panel/index.wxml
<view class="filter-panel">
<view class="panel-container">
<view class="panel-left">
...
</view>
<view class="panel-right">
...
</view>
</view>
</view>
/components/filter-panel/index.json
{
"component": true,
"usingComponents": {}
}
组件外部:
假如我要在index.wxml里引入组件:
<filter-panel></filter-panel>
index.json
{
"usingComponents": {
"filter-panel": "/components/filter-panel/index"
}
}
这样就成功引入组件啦~(说真的组件化做好了非常舒服,后期会省很多力气)
2.组件与外部的数据传递
(1) 固定数据渲染
组件外部:
index.wxml
<filter-panel list="{{list}}" active="{{active}}"></filter-panel>
index.js
data: {
list: [
['附近', '地铁站'],
[['不限', '1km', '2km', '3km'], ['江汉路', '积玉桥', '洪山广场', '楚河汉街', '光谷广场']]
],
active: [0, 0]
},
组件内部:
/components/filter-panel/index.js
Component({
/**
* 组件的属性列表
*/
properties: {
list: Array,
active: Array
},
/**
* 组件的方法列表
*/
methods: {
...
}
})
如果想从组件外向组件内传递数据,直接在外部引用时以属性的方式传入。
这里我传入了2个属性:
list是二级选择面板渲染的数据。
active是用户选择的选项数据。
到这里组件已经可以正常展示了,但是点击显示选中项还未实现。
(2) 可变数据渲染
控制组件active项的是外部的数据active: [0, 0],通过组件以属性的形式传到了内部。
/components/filter-panel/index.wxml
<view class="filter-panel">
<view class="panel-container">
<view class="panel-left">
<ul>
<li
class="{{active[0] == index? 'active': ''}}"
wx:for="{{list[0]}}"
wx:key="{{index}}"
wx:index="index"
data-index="{{index}}"
bindtap="changeLevel1"
>
{{item}}
</li>
</ul>
</view>
<view class="panel-right">
<ul>
<li
class="{{active[1] == index? 'active': ''}}"
wx:for="{{list[1][active[0]]}}"
wx:key="{{index}}"
wx:index="index"
data-index="{{index}}"
bindtap="changeLevel2"
>
{{item}}
</li>
</ul>
</view>
</view>
</view>
/components/filter-panel/index.js
Component({
/**
* 组件的属性列表
*/
properties: {
list: Array,
active: Array
},
/**
* 组件的方法列表
*/
methods: {
changeLevel() {
this.triggerEvent('changeLevel', {
level1: this.properties.active[0],
level2: this.properties.active[1]
})
},
changeLevel1(event) {
const index = event.target.dataset.index
this.setData({
active: [index, 0]
})
this.changeLevel()
},
changeLevel2(event) {
const level2 = 'active[1]'
const index = event.target.dataset.index
this.setData({
[level2]: index
})
this.changeLevel()
}
}
})
(3) 组件内数据传到外部
在这个组件内我定义了changeLevel这个方法,每次点击一级菜单或二级菜单的时候我就用过this.triggerEvent方法把active的值传到组件外部以供使用。
/components/filter-panel/index.js
methods: {
changeLevel() {
this.triggerEvent('changeLevel', {
level1: this.properties.active[0],
level2: this.properties.active[1]
})
},
changeLevel1(event) {
const index = event.target.dataset.index
this.setData({
active: [index, 0]
})
this.changeLevel()
},
changeLevel2(event) {
const level2 = 'active[1]'
const index = event.target.dataset.index
this.setData({
[level2]: index
})
this.changeLevel()
}
}
五、总结
这个项目里倒是没用用到组件间的数据传递,所以只是组件和外部的传递,还算是比较简单,但是一定要思考清楚数据的变化状态。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。