在某些场景,用到组件还是比较多的。例如,在页面的底部有一个悬浮,悬浮分两种,每种里边有四个按钮,每个按钮分为登录和没登录两个状态,点击每个按钮绘根据后台传的值的状态不同分别请求不同的接口(当然了,有些处理完页面也可能是跳转,或者跳转webview,或者reLaunch某个页面......)。总之一句话,这个底部悬浮比较麻烦,考虑的场景比较多,然后呢,好多页面都会用到,不用写那么多的重复代码,写好一个组件,大家都来调用,用的简单,维护也简单。
下边我用一个简单的场景来举例,复杂场景原理相同,只不过是根据业务场景再做处理即可。
我的场景:页面中的按钮存在登录和未登录两个状态,组件中的按钮也存在这两个状态,任何一处授权登录过了,整个小程序的登录状态都会改变。此时若把父组件的登录状态改变,就要传给子组件;子组件的登录状态改变,父组件也跟着变。但父子组件又相互独立,此时就要通过某种方式来相互传值。
1.组件('component/btn/btn’)
1.1.wxml
<view wx:if="{{isLogin}}" class='btn' catchtap='clickme'>子 组件的view</view>
<button wx:if="{{!isLogin}}" class='btn' open-type="getPhoneNumber" lang="zh_CN" bindgetphonenumber="getPhoneNumber">子 组件的button</button>
1.2.wxss
.btn{
width: 250rpx;
height: 50rpx;
line-height: 50rpx;
text-align: center;
background-color: orange;
font-size: 20rpx;
color: #fff;
margin-left: 0;
border-radius: 10rpx;
padding: 0 20rpx;
box-sizing: border-box;
margin: 20rpx 20rpx;
}
.btn::after{
border: 0;
}
1.3js
const app = getApp();
Component({
behaviors: [],
// 属性定义(详情参见下文)
properties: {
isLogin: {
type: Number,
value: 0,
}
},
data: {
}, // 私有数据,可用于模板渲染
lifetimes: {
// 生命周期函数,可以为函数,或一个在methods段中定义的方法名
attached: function () {
// console.log(111111111111)
var that = this;
},
moved: function () {
// console.log(2222222222222)
},
detached: function () {
// console.log(3333333333333)
},
},
// 生命周期函数,可以为函数,或一个在methods段中定义的方法名
attached: function () {
// console.log(4444444444444)
},
ready: function () {
// console.log(5555555555555555)
},
pageLifetimes: {
// 组件所在页面的生命周期函数
show: function () {
// console.log(666666666666666666)
var that = this;
},
hide: function () {
// console.log(777777777)
},
resize: function () {
// console.log(88888888888888)
},
},
methods: {
clickme(e,aa='子请求接口'){
console.log(aa)
},
getPhoneNumber: function (e) {
var that = this;
if (e.detail.errMsg == "getPhoneNumber:ok") {
var param = {
iv: e.detail.iv,
encryptedData: e.detail.encryptedData,
logintype: 2
}
/**logintype:2时授权登录**/
that.setData({
isLogin: true,
});
that.clickme(e,'子授权后请求接口')
var loginState = {
isLogin: 1
};
that.triggerEvent("triggerBottom", loginState);
} else {
wx.showToast({
title: '子授权失败',
icon: 'none',
duration: 3000
});
}
},
},
});
1.4json
{
"component": true,
"usingComponents": {}
}
2.调用组件的文件
2.1wxml
<view wx:if="{{isLogin}}" class='btn'>父 组件的view</view>
<button wx:if="{{!isLogin}}" class='btn' open-type="getPhoneNumber" lang="zh_CN" bindgetphonenumber="getPhoneNumber">父 组件的button</button>
<fixbottom isLogin="{{isLogin}}" bind:triggerBottom="triggerBottom"></fixbottom>
2.2wxss
.btn{
width: 250rpx;
height: 50rpx;
line-height: 50rpx;
text-align: center;
background-color: orange;
font-size: 20rpx;
color: #fff;
margin-left: 0;
border-radius: 10rpx;
padding: 0 20rpx;
box-sizing: border-box;
margin: 20rpx 20rpx;
}
.btn::after{
border: 0;
}
2.3js
var that;
Page({
/**
* 页面的初始数据
*/
data: {
isLogin: 0
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function(options) {
that = this;
},
triggerBottom(e) {
var isLogin = e.detail.isLogin == 1 ? e.detail.isLogin : 0;
that.setData({
isLogin: isLogin
});
},
clickme(e, aa = '父请求接口') {
console.log(aa)
},
getPhoneNumber: function(e) {
var that = this;
if (e.detail.errMsg == "getPhoneNumber:ok") {
var param = {
iv: e.detail.iv,
encryptedData: e.detail.encryptedData,
logintype: 2
}
/**logintype:2时授权登录**/
that.setData({
isLogin: true,
});
that.clickme(e, '父授权后请求接口')
} else {
wx.showToast({
title: '父授权失败',
icon: 'none',
duration: 3000
});
}
},
})
2.4json
{
"usingComponents": {
"fixbottom": "/component/btn/btn"
}
}
3.说明
3.1小程序和vue类似,双向数据绑定,组件其实就是一个标签,在这个标签上可以绑定一个数据,可以绑定一个事件,当然了,这个绑定的数据(或者事件)都须要遵从组件的规则。
3.2 父集向子集传值,直接传就好了,父集变子集跟着变。子集的值变,要想派发给父集,必须通过‘triggerEvent’来实现,派发出去。
3.3 现在的组件生命周期好像存在一些问题,第一次进入只执行了lifetimes里的attached和ready两个,pageLifetimes里的show没执行,看文档的意思pageLifetimes应该是和页面的声明周期同步,但没有(可能是个bug吧,嘻嘻)。但当页面被back时会执行show生命周期(感觉还是有点不靠谱)。
3.4对于一进页面和back回来都需要检测登状态(或者其他的状态值的)建议在attached中执行一次接口请求,然后再show中也执行一次(如果这个值在缓存中,可以做一个判断,如果为真就不再请求,否则会请求),不然就会不靠谱,哈哈。
3.5在写回调方法时,注意一下写法,否则会出现意想不到的惊喜哦
dayin(aa='默认参数'){
console.log(aa)
},
dioyong(cb) {
typeof cb == "function" && cb();
},
right(cb) {
var that = this;
this.dioyong(function(){
that.dayin('this is right')
});
},
wrong() {
var that = this;
this.dioyong(that.dayin('this is wrong'))
}
例如上边的right方法和wrong方法,看着好像都对,你自己在本地调用运行一下。
直接写结论了,right方法确确实实是当回调函数来执行的,wrong方法并不是,尤其是在接口调用,涉及接口调用顺序的情况就会出错。wrong里的调用,加了括号,相当于函数调用语句直接执行了,就没走 typeof cb == "function" && cb() 。而right确实是一个function(哈哈,应该是这个原因吧)。