送给写小程序的你。

声明:本人也是菜鸟一只,写的不好的地方,大佬们指点指点。

微信小程序商城,持续更新。先说说写小程序的渊源吧,我之前认识的大佬写了一个比较简单的小程序,我改了改样式于是成功的有了我的第一个小程序。
然后我在找工作的时候,简历上写了这么一回事,结果进来之后,leader 语重心长的对我说,公司的小程序商城就教给你了,重点是公司没有一个人会微信小程序。WTF,不是招我进来写 VUE 的吗? 于是就开始了我从零开始写微信小程序商城之路。
微信小程序的官方文档什么的我就不说了,那是肯定要看的。说说我这几天开始写我遇到的问题。希望对将要写小程序的你能有一丝丝帮助,因为本人也很菜。

微信小程序之 rpx

在微信官方的文档中,我们看到这样一句话:在 iPhone 6 上,屏幕宽度为 375px,共有 750 个物理像素,则 750rpx = 375px = 750 物理像素,1rpx = 0.5px = 1 物理像素。
开发微信小程序时设计师可以用 iPhone 6 作为视觉稿的标准。也就是说,设计师在设计小程序时,可以这样做:直接以 iPhone 6 的屏幕尺寸(375×667)用作视觉稿尺寸,1 px = 0.5 rpx;以 1 px = 1 rpx 的标准,将设计稿尺寸设定为 750×1334。
其实总的来说就是,你跟 UI 小姐姐说一声,然后就可以很爽的写样式了。

微信小程序之容器视图

view 其实就是 div,嘿嘿嘿就是这么简单粗暴的解释。
scroll-view 可滚动视图区域。 这个需要注意的是,横向滚动的视图区域。重要的是这两个行内样式,其他的官方文档也说的比较清楚。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<view class="supply-scroll bg">
<scroll-view
class="scroll-box"
scroll-x="true"
style="width: 100%;white-space: nowrap; display: flex"
>
<view
class="scroll-items"
wx:for="{{scrollList}}"
wx:key="index"
data-index="{{index}}"
style="display: inline-block"
>
<image src="{{item.url}}" class="scroll-image" />
</view>
</scroll-view>
</view>

微信小程序之 UI 框架

这个,很有意思,当我写完一个我觉得很有灵性的选项卡的时候,以前哥们告我微信小程序有个框架你看看去。。。

1
2
3
<view wx:for="{{tab}}" wx:key="index" data-id="{{item.id}}" data-index="{{index}}"   bindtap="switchTab" class="tab-list {{tabIndex === index?'on':''}}">
{{item.name}}
</view>


不 BB,直接上地址 weui

先写到这里,周六加班的午休时间,冒着被同时打死的危险写的,如果有什么写的不好的地方,请大家多多包涵,我也还会分享我遇到的问题。
最近又看到有赞的微信小程序 UI 还是老规矩直接上地址:
zanui-weapp

微信小程序之 switchTab

页面跳转大家官网上看的 navigator 用法你套着用就可以了,今天分享一下我在小程序中遇到的页面跳转的问题。我写的是一个电商商城小程序,跳转到详情页之后有两个点击跳转的 icon 如图:

聪明的你,肯定猜出来这两个 icon 是跳转到哪的,肯定也是 bindtap,然后 wx.navigator 就搞定了。两个简单的页面跳转嘛,然后居然就是这两个问题卡了我一个多小时,后来还是在微信群里问大佬们才解决的,因为自己看文档不仔细,跳转的这两个页面是微信官方底部 tab 的路径如图:

问题是出在官方的 tabbar 中你用了这几个路径如图:


然后解决方法为:

1
2
3
4
5
6
handleGoIndex: function() {
wx.switchTab({url: '/pages/index/index'})
},
handleGoCart: function() {
wx.switchTab({url: '/pages/cart/index'})
}

微信小程序之禁止页面下拉

有的时候你会遇到不让页面下拉,如果你以为我说的是下拉刷新的api的话那就图样了。就是不让你下滑的时候出来那个白条。直接上代码了:

1
2
3
4
<view catchtouchmove="stopDrag" class="container">
stopDrag(){
return false;
}

但是这个慎用,我也想在这里如果有大神能指点指点就最好了,这个的效果是可视区域全部静止滑动… 拼多多的小程序首页就是不会出现那个下拉的白条,但能上滑看商品信息,求教~

最新解决方法,在app。json 里面增加一条这个属性,就可以禁止滚动。

微信小程序之页面深度

在测试自己的项目的时候,发现明明逻辑没问题,语法也没问题。突然想起来小程序的页面栈是五个,于是特别中二的数了一下自己的页面跳转哈哈… 才发现当时自己的理解是错误的,刚开始不细心看文档,我以为这五个页面是超过五个会把新的加进页面栈,第一个打开的销毁。我就无脑用 wx.navigateTo,遇到这个问题后用了 wx.redirectTo 就解决了。但是谁让我好学呢,于是发现一个大神写的特别完美的理解小程序页面跳转的原理。
(现在深度,变成了 10 层,但是在 ios 10 的部分系统下,还是有 bug 的,还是要考虑页面深度这种场景的。)
小程序路由

微信小程序之上拉加载

我现在公司的项目做的是电商的小程序,下拉刷新,官网写的很明确不能与 scroll-view 同时使用。我是在脑子瓦特的情况下,onReachBottom 和 scroll-view 的 bindscrolltolower 都用了。经过我的实践,推荐大家用 onReachBottom。如果是一些 tab 里面也有下拉刷新,这个就很方便直接上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
onReachBottom() {
let isPush = this.data.index,
val = this.data.inputVal;
this.setData({
isBtnShow: true
});
if (isPush ==1) {
let num = this.data.limitIndex;
this.setData({
limitIndex: num+1
})
//关于上拉加载的性能优化
setTimeout(()=>{
// 给后端传下拉刷新的次数+1
const data = {
limitIndex: this.data.limitIndex
};
utils.sendRequest(api.AllGoodsUrl, data, this.handleReachBottom.bind(this));
},1500)
};
if (val != '') {
setTimeout(()=>{
let num = this.data.limitIndex;
this.setData({
limitIndex: num+1
})
// 给后端传下拉刷新的次数+1
const data = {
limitIndex: this.data.limitIndex,
data:{
name: this.data.inputVal,
}
};
utils.sendRequest(api.AllGoodsUrl, data, this.handleLoadMore.bind(this));
},1500)
};
},
这段代码主要就是判断tab的状态来延时请求接口。实现效果如下图:



这就是我用上拉加载的实践,希望有大神能提出更好的建议。

微信小程序之数据交互

说到这里顺便把我的一些关于数据交互的一些经验分享一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function sendRequest(path, data, callback) {
wx.request({
url: path,
data: data,
header: {
'content-type': 'application/json'
},
method: 'POST',
success: callback,
fail: res => {
console.log(res);
}
});
}
把微信请求封装起来;
还有项目里面要用到的接口如下;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function promiseRequest(url, data = {}) {
return new Promise(function (resolve, reject) {
wx.request({
url: url,
data: data,
method: 'POST',
header: {
'Content-Type': 'application/json',
},
success:(res) => {
if (res.statusCode == 200) {
resolve(res);
} else {
reject(res.errMsg);
}

},
fail: (err) => {
reject(err)
console.log("failed")
}
})
});
}
新增 promise 请求方式
//没有参数就传空
utils.promiseRequest(api.BannerUrl).then(res => {
if(res.data.error == 0){
//dosomething。。。
}else {
utils.showModal(res.data.err_msg)
}
});




希望有大神能帮忙一下小程序的组件化最佳实践~
(现在wepy和mpvue 都挺靠谱的 /捂脸)

微信小程序之Android请求失败的坑

我当时遇到的情况是,在模拟器和IOS环境下请求数据都是没有任何问题的,在测试android环境时发现请求失败如下图

这个错还让我跟运维的大哥撕了半天哈哈, 首先出现这个错,兄弟这个锅完完全全就能甩给运维的大哥,不留任何余地。 然后可以帮大哥提供一写资料,一步步排查,先查你项目绑定域名:
1、https证书问题 :ssl证书配置需要使用pem,不使用crt, ,
2、服务器端的版本信息:TLS版本的问题,该问题在微信小程序官方文档中已经提及到,服务器TLS版本必须支持 1.2 (启用1.2,禁用1.1和1.0等低版本),
3、前两种方法是在查阅资料有人说是可解决的,但是对于我们公司最后在排查前两项之后,运维大哥用Nginx转发了之后,解决了安卓请求失败的问题。希望可以帮到大家吧~

微信小程序之地址页面三级联动

这个问题,现给大家贴一个地址,非常感谢大神的开源
小程序路由
大家可以看到代码之后,先理清你要处理的后端数据和存地址的要求等等,我遇到的问题是
后端给我的数据里面有每一个地址对应的 ID,比如北京:2 北京:50 东城区:500
处理这个数据的时候,我根据原来大神的代码做了一些修改.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
let val = e.detail.value,
t = this.data.values,
cityData = this.data.cityData,
index = this.data.id,
list = this.data.addrList;
list[index].area = true;
try {
if (val[0] != t[0]) { //当val是选择省份的时候
const citys = [];
const countys = [];
cityData[val[0]].child.map(item => citys.push({name:item.name,id:item.id}));
cityData[val[0]].child[0].child.map(item => countys.push({name:item.name,id:item.id}));
list[index].provinceName = this.data.provinces[val[0]].name;//省份
list[index].cityName = cityData[val[0]].child[0].name;//城市
list[index].districtName = cityData[val[0]].child[0].child[0].name;//地区
list[index].province = this.data.provinces[val[0]].id;//对应的传值ID
list[index].city = cityData[val[0]].child[0].id;//对应的传值ID
list[index].district = cityData[val[0]].child[0].child[0].id;//对应的传值ID
this.setData({
citys: citys,
countys: countys,
values: val,
value: [val[0], 0, 0],
addrList: list
})
return;
}
if (val[1] != t[1]) {//当val是选择城市的时候
const countys = [];
cityData[val[0]].child[val[1]].child.map(item => countys.push({name:item.name,id:item.id}));
list[index].cityName = this.data.citys[val[1]].name;// 选择城市
list[index].city = this.data.citys[val[1]].id;//对应的传值ID
list[index].districtName = cityData[val[0]].child[val[1]].child[0].name;//选择城市对应的地区
list[index].district = cityData[val[0]].child[val[1]].child[0].id;//对应的传值ID
this.setData({
countys: countys,
values: val,
value: [val[0], val[1], 0],
addrList: list
})
return;
}
if (val[2] != t[2]) {//当val是选择地区的时候
list[index].districtName = this.data.countys[val[2]].name;//选择地区
list[index].district = this.data.countys[val[2]].id;//对应的传值ID
this.setData({
county: this.data.countys[val[2]].name,
values: val,
addrList: list
})
return;
}

} catch(e) {
// statements
console.log(e);
}
list里面是有 收货人,电话,等等信息 但是我只操作改变数组里面地址改变的信息,


布局方面需要做一些修改的地方就是


我贴了这么多图,是因为我真不会说了,调这个页面调了两天。只能提供个大概方向,还是得一步步处理数据,goodluck~

微信小程序之 Android 环境下的横向滚动

1
2
3
4
5
6
7
8
9
10
<view class="tab bg">
<scroll-view class="" scroll-x="true" style="width: 100%;white-space: nowrap; display: flex;overflow-x: auto;">
<view style="display: inline-block" bindtap="switchIndex" class="tab-list {{index === 1 ?'on':''}}">首页</view>
<view wx:for="{{tab}}" wx:key="index" data-id="{{item.id}}" data-index="{{index}}"
style="display: inline-block" bindtap="switchTab" class="tab-list {{tabIndex === index?'on':''}}">
{{item.nav_name}}
</view>
</scroll-view>
</view>
代码一贴其实当测试小哥告诉你安卓tab不能滑的时候,你只需要加一个overflow-x: auto;哈哈哈

微信小程序之 Ios 环境下 mp4 播放问题

当你遇到你的 mp4 格式有的能播放,有的不能播放的话,你只需要看一下你的 mp4 编码格式,必须是 h264 格式才行。

微信小程序之微信支付的坑

这个因为我也是第一次写小程序嘛,后端的大哥,在 H5 的商城里什么都实现了一次了,非说各种没问题,只贴两个图就明白了第一个

这个 api 里面的所有数据都是后端返给你的,不要接受他的甩锅哈哈。
还有就是一定要让后端好好看微信支付的文档,一般公司开发商城都是服务商版的支付服务,这里就是我和后端大哥的甩锅之路,他没有绑定我得小程序 appid,然后各种说调不通。。。

去这里配置好,前端只需要调 API 传值就好

微信小程序之图片上传

老规矩,直接上代码了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
handleCancelPic() {
let id = this.data.dbId;
wx.chooseImage({
count: 3, // 默认9
sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: res => {
// 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片
var tempFilePaths = res.tempFilePaths;

this.setData({
src: tempFilePaths
})
upload(this,tempFilePaths,'','');
}
})
}
然后一个封装好的方法
function upload(page, path,way,id) {
console.log(path)
wx.showToast({
icon: "loading",
title: "正在上传"
});
var test = [],
that = this;
for (var i = 0; i<path.length; i++) {
wx.uploadFile({
url: api.CancelImg,
filePath: path[i],
name: 'file',
header: { "Content-Type": "multipart/form-data" },
success: res => {
test.push(res);
wx.setStorageSync('cancelImg',test)
console.log(test)
if (res.statusCode != 200) {
wx.showModal({
title: '提示',
content: '上传失败',
showCancel: false
})
return;
}else {
wx.showModal({
title: '提示',
content: '上传成功',
showCancel: false
})
}
},
fail: function (e) {
console.log(e);
wx.showModal({
title: '提示',
content: '上传失败',
showCancel: false
})
},
complete: function () {
wx.hideToast(); //隐藏Toast
}
})
}
这个是多个图片上传的方法,单个图片上传的话,把循环去掉就好。主要是因为微信官方默认的就是一次上传一张图片这个很蛋疼。只能这么搞了。。。

微信小程序之电商购物车逻辑

接着再给大家分享一个关于小程序购物车全选的逻辑处理,这个还是要感谢我的老妹教导我,一个开发人员做东西一定要严谨,不管 UI 设计的有多丑,该有的逻辑你一定要做到。

首先我们要做到的就是,当用户点击第三个商品时 全选按钮自动选中,或者全选之后,只要有一个商品不选中,全选按钮也得变动。先给大家看一下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
你要在页面onload时候定义一些你需要每次渲染的数据
data: {
likeList: [],
carts:[], // 购物车列表
hasList:false, // 列表是否有数据
//totalPrice:0, // 总价,初始为0
selectAllStatus:false, // 全选状态,默认全选,
goodsNums:0,
allclick:[]
}
每件商品单个选中的的逻辑处理
selectList(e) {
const index = e.currentTarget.dataset.index;// 获取每一个点击的购物车ID
let carts = this.data.carts,
selected = carts[index].select,
all = this.data.allclick;
carts[index].select = !selected;
carts[index].select == true ? all.push(index):all.splice(index,1);
all.length == carts.length ?
this.setData({
selectAllStatus: true
}):this.setData({
selectAllStatus: false
});
this.getTotalPrice();
},
上面的代码,先做的就是单选的页面渲染效果。判断部分的代码就是最主要的处理全选逻辑的一步。相信你看到这里也注意到我在data里定义了一个allclick的空数组,然后就是接下来的逻辑:

按钮选中时取出对应 item 的角标放到新的 arr 里,这里因为我之前结算的逻辑已经搞好了,我就随便往数组里 push 数据,但其实可以作为对应商品的更重要的一些数据处理。按钮不选中是从新的 arr 里找到这个 item 对应下标的数据移除完成上面两步处理之后,每次按钮状态发生变化的时候判断 arr 的长度和 cart 的长度。
这就是我的处理,也可循环,实现的方式有很多,只是拿出来让没有接触过的小伙伴做个参考~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
data: {
likeList: [],
carts:[], // 购物车列表
hasList:false, // 列表是否有数据
//totalPrice:0, // 总价,初始为0
selectAllStatus:false, // 全选状态,默认全选,
goodsNums:0,
allclick:[]
}
每件商品单个选中的的逻辑处理
selectList(e) {
const index = e.currentTarget.dataset.index;// 获取每一个点击的购物车ID
let carts = this.data.carts,
selected = carts[index].select,
all = this.data.allclick;
carts[index].select = !selected;
carts[index].select == true ? all.push(index):all.splice(index,1);
all.length == carts.length ?
this.setData({
selectAllStatus: true
}):this.setData({
selectAllStatus: false
});
this.getTotalPrice();
},
这段代码也还是先处理全选的状态,然后就是关联状态的处理,

当全选没有勾选的时候全部改变商品信息里的按钮为 false,直接清空 allclick 数组。
当全选勾选的时候全部改变商品信息里的按钮为 true,先清空,接着重新 push,再赋值。
经过这几步操作之后就解决了全选这方面的所有逻辑,

微信小程序之使用 Map 对象

这是我的梁 master 跟我提的这件事,我是小程序,她是 Android,项目一样。我俩关系好,然后她教我的用的这个 Map 对象,其实这个是 java 的 map 对象。用起来真的很爽,不废话了直接上代码。
这个在 es6 的标准里面也推出了 Map 对象。借用阮大神书里面的一段话:

1
2
3
4
5
6
7
8
9
JavaScript 的对象(Object),本质上是键值对的集合(Hash 结构),但是传统上只能用字符串当作键。这给它的使用带来了很大的限制。

const data = {};
const element = document.getElementById('myDiv');
data[element] = 'metadata';
data['[object HTMLDivElement]'] // "metadata"

上面代码原意是将一个 DOM 节点作为对象data的键,但是由于对象只接受字符串作为键名,所以element被自动转为字符串[object HTMLDivElement]。
为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,MapObject 更合适。

感兴趣的小伙伴可以移步es6 入门 map set

最佳实践,需求是点击每一个分类,在点击过后如果数据返回没有变化,就不请求接口。 来实现一些优化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let localMap = new Map(),// 定义一个全局的MAP对象
typeCode = ''; // 点击分类里面的细分选项卡
handleClickTabs(e) {
let id = e.target.dataset.index,
code = e.target.dataset.id;
typeCode = code;// 这里定义code,在请求回调里面使用
this.setData({ leftTab : id });
if (localMap!=null) {// map对象
let list = localMap.get(typeCode);//获取对应分类的type的code
if (list!=null) {//map里面有值,渲染页面
this.setData({ tabSonList:list }) }else {//map里面没有值,去请求接口 const data = { goodsTypeCode: code };
utils.sendRequest(api.ClassifySon, data, this.handleGoodsSon.bind(this)); }
}else { const data = { goodsTypeCode: code };
utils.sendRequest(api.ClassifySon, data, this.handleGoodsSon.bind(this)); } }, //分类里面的内容 handleGoodsSon(res) {
let list = res.data; localMap.set(typeCode,list)//存对应typeCode的list
this.setData({ tabSonList:list }) },


这样就实现了,对选项卡的优化,如果接口数据变多了的话,会重新赋值。 虽然是自己给自己加戏,但这是也是我的一种工作态度,学习了新东西,还让优化了项目。

微信小程序之全局变量缓存的问题

这个问题的出现是因为,在 input 的事件,我没有找到更好的能监听键盘收回的方法,也是为了更好的用户体验吧,所以随之而来的问题就是全局变量,在赋值之后用户退出这个页面,全局变量被微信缓存了,然后造成,用户输入过一次之后,修改手机号失败的 bug。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
let Btel = '',//防止用户恶意修改手机号
Byzm = '';

handleTel(e) {// 这里修改用户输入的手机号
this.setData({
tel:e.detail.value
})
},
handleYzm(e) {
this.setData({
Yzm:e.detail.value
})
Byzm = e.detail.value;
let psd = this.data.psd,
yzm = this.data.Yzm;
yzm == psd ? this.setData({isYzm:0}):'';
},
handleGetYzm() {
let reg = common.telReg,
val = this.data.tel;
if (!reg.test(val)) {
wx.showModal({
content:'请输入正确的手机号',
showCancel:false,
confirmColor:'#3cc51f',//默认值为#3cc51f
success:res =>{
if(res.confirm){
this.setData({
tel:''
})
}
}
})
}else {//发送验证码的时候用全局变量的手机号
Btel = val;// 这里是全局的手机号
this.setData({
isReset : true,
isNoClick: true
})
const data ={
tel:val//传的是全局变量
};
utils.sendRequest(api.YanZhengMa, data, this.handleGetYzmSucc.bind(this));
//button 定时器
let time = setInterval(()=>{
let phoneCode = this.data.time;
phoneCode --
this.setData({
time : phoneCode
})
if(phoneCode == 0){
clearInterval(time)
this.setData({
isReset : false,
isNoClick: false,
time:60
})
}
},1000)
}


},

//然后这一步是校验了用户在请求完验证码接口后 有没有修改手机好 然后保存
handleSave() {
let name = this.data.name,
telNum = this.data.tel,
yzm = Byzm,
status = this.data.isYzm,
card = wx.getStorageSync('UserCard');
let timestamp= new Date().getTime();
if (yzm == '') {
wx.showModal({
content:'请输入验证码.',
showCancel:false,
confirmColor:'#3cc51f'
})
return false;
}
if (name!=''&&telNum!='') {
if(Btel != telNum) {
utils.showModal('手机号发生变化,请重新获取验证码。');
}else {
const data ={
distribution_id:card.distribution_id,
post:{
user_name:name,
user_tel:Btel,
user_code:yzm
},
user_id:card.user_id,
password:yzm+timestamp
};
utils.sendRequest(api.BindTel, data, this.handleSaveTel.bind(this));
}
}else {
utils.showModal('请填写完整信息哟');
}
},

// 然后在 保存成功之后 用户点击确定 清空 全局变量 也可以在隐藏和卸载的生命周期里面清空全局变量。
handleSaveTel(res) {
if (res.data.error == 0) {
let go = this.data.go,
id = res.data.data.id,
lv = res.data.data.level;
wx.showModal({
content:'绑定成功~',
showCancel:false,
confirmColor:'#3cc51f',//默认值为#3cc51f
success:res =>{
if(res.confirm){
Byzm = '';//对小程序全局变量缓存进行清除
if (go) {
wx.redirectTo({
url: '/pages/user/cash/cash'
})
}else {
if (id != 0) {
let card = wx.getStorageSync('UserCard');
card.distribution_id = id;
card.distribution_level = lv;
wx.setStorageSync('UserCard',card);
wx.setStorageSync('seller', true)
}
wx.switchTab({
url: '/pages/user/index'
})
}
}
}
})
return false
}else {
utils.showModal(res.data.err_msg);
}
}

微信小程序之全局变量缓存的问题

这个问题的出现是因为,在 input 的事件,我没有找到更好的能监听键盘收回的方法,也是为了更好的用户体验吧,所以随之而来的问题就是全局变量,在赋值之后用户退出这个页面,全局变量被微信缓存了,然后造成,用户输入过一次之后,修改手机号失败的 bug。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
let Btel = '',//防止用户恶意修改手机号
Byzm = '';

handleTel(e) {// 这里修改用户输入的手机号
this.setData({
tel:e.detail.value
})
},
handleYzm(e) {
this.setData({
Yzm:e.detail.value
})
Byzm = e.detail.value;
let psd = this.data.psd,
yzm = this.data.Yzm;
yzm == psd ? this.setData({isYzm:0}):'';
},
handleGetYzm() {
let reg = common.telReg,
val = this.data.tel;
if (!reg.test(val)) {
wx.showModal({
content:'请输入正确的手机号',
showCancel:false,
confirmColor:'#3cc51f',//默认值为#3cc51f
success:res =>{
if(res.confirm){
this.setData({
tel:''
})
}
}
})
}else {//发送验证码的时候用全局变量的手机号
Btel = val;// 这里是全局的手机号
this.setData({
isReset : true,
isNoClick: true
})
const data ={
tel:val//传的是全局变量
};
utils.sendRequest(api.YanZhengMa, data, this.handleGetYzmSucc.bind(this));
//button 定时器
let time = setInterval(()=>{
let phoneCode = this.data.time;
phoneCode --
this.setData({
time : phoneCode
})
if(phoneCode == 0){
clearInterval(time)
this.setData({
isReset : false,
isNoClick: false,
time:60
})
}
},1000)
}


},

//然后这一步是校验了用户在请求完验证码接口后 有没有修改手机好 然后保存
handleSave() {
let name = this.data.name,
telNum = this.data.tel,
yzm = Byzm,
status = this.data.isYzm,
card = wx.getStorageSync('UserCard');
let timestamp= new Date().getTime();
if (yzm == '') {
wx.showModal({
content:'请输入验证码.',
showCancel:false,
confirmColor:'#3cc51f'
})
return false;
}
if (name!=''&&telNum!='') {
if(Btel != telNum) {
utils.showModal('手机号发生变化,请重新获取验证码。');
}else {
const data ={
distribution_id:card.distribution_id,
post:{
user_name:name,
user_tel:Btel,
user_code:yzm
},
user_id:card.user_id,
password:yzm+timestamp
};
utils.sendRequest(api.BindTel, data, this.handleSaveTel.bind(this));
}
}else {
utils.showModal('请填写完整信息哟');
}
},

// 然后在 保存成功之后 用户点击确定 清空 全局变量 也可以在隐藏和卸载的生命周期里面清空全局变量。
handleSaveTel(res) {
if (res.data.error == 0) {
let go = this.data.go,
id = res.data.data.id,
lv = res.data.data.level;
wx.showModal({
content:'绑定成功~',
showCancel:false,
confirmColor:'#3cc51f',//默认值为#3cc51f
success:res =>{
if(res.confirm){
Byzm = '';//对小程序全局变量缓存进行清除
if (go) {
wx.redirectTo({
url: '/pages/user/cash/cash'
})
}else {
if (id != 0) {
let card = wx.getStorageSync('UserCard');
card.distribution_id = id;
card.distribution_level = lv;
wx.setStorageSync('UserCard',card);
wx.setStorageSync('seller', true)
}
wx.switchTab({
url: '/pages/user/index'
})
}
}
}
})
return false
}else {
utils.showModal(res.data.err_msg);
}
}

小程序之微信支付的深坑

事情是这样的,因为公司主体变更,避税之类的事。我们公司的小程序,需要重新换绑微信支付,在财务给我们申请号商户号之后,我们排期半夜来切环境。 我记得是一个周四的晚上,十二点开始切环境,然后 大概后端代码数据库都准备好之后,我们重新绑定微信支付,以为大功告成。周五还能弹性一上午美滋滋~ 没想到 刚交个测试,说微信支付不能用,提示我们没有授权。wtf,刚绑定好的你这样提示,不合适吧。按照报错一查,我去小程序 社区里一看。你敢信,微信开放平台的商户号和公众号商户号,是两回事。 小程序之支持公众号的商户号,但是你在开放平台绑定小程序微信支付的时候,官方给你的提示是 绑定成功~。然后 就变成了 早上七点下班。。。 血的教训呀!!!

小程序之登陆改版

最近因为小程序官方,逐步废除 UserInfo 接口的这个决定,让很多微信小程序的开发者,很烦恼,同时我也因为这个上火,公司有重要会议要展示,突然来着这么一招。真的是够了,但是没办法,开始改吧。于是我,文档小程序•小故事(4)–获取用户信息 | 微信公众平台 你会发现跟原来的方式,只是改变了 userinfo 的数据返回。 login 的接口拿 res.code,然后点击登陆按钮的返回值相结合,就可以换取到 unionid,来开始业务逻辑了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function login(userInfo,callback,reset) {
wx.login({
success: res => {
// 发送 res.code 到后台换取 openId, sessionKey, unionId
if(res.code){
//存用户信息
wx.setStorageSync('UserInFo', userInfo)
// 扫面二维码进入
let Scene = wx.getStorageSync('scene')
let userInfoStr = JSON.stringify(userInfo);
wx.request({
url: api.WxUnionId,
data: {
appid: 'xxx',//测试
secret: 'xxx',
js_code: res.code,
grant_type: 'authorization_code',
userInfo: userInfoStr,
scene:Scene//这个是我们的业务逻辑的参数 忽略
},
method: 'POST',
header: {
'content-type': 'application/x-www-form-urlencoded'
},
success: callback
})
}else{

}
}
})

封装好这个,就可以开始用 button 来开始弹授权的吐司了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<button class =“login-btn”open-type =“getUserInfo”bindgetuserinfo =“hand
leUserInfo”>
登录
</ button>

// 然后打印events 你就可以加密数据 数据 调接口 后端解析 换 unionId
handleUserInfo(e) {
let msg = e.detail.errMsg;
if(msg === 'getUserInfo:ok'){
let user = e.detail.userInfo;
user.encryptedData = e.detail.encryptedData;
user.iv = e.detail.iv;
utils.setDataBase('islogin',true);//set 本地数据
utils.login(user,this.handleLogin.bind(this))
this.setData({
islogin:false
});
}else{
utils.setDataBase('islogin',false);//set 本地数据
this.setData({
islogin:true
});
utils.showModal('xxx,申请获得您的公开信息(头像,昵称等)。授权后,您能体验到我们更完善的功能,谢谢您关注五色糖。',false,this);
}
},
在handleLogin 里面 拿到后端的res 就可以开始业务逻辑了。涉及到业务逻辑 就不贴代码了。

因为我当时,是一个人写电商商城,那会 mpvue 没有出 wepy 公司没有留学习的时间,以上的代码全是原生的写法,但是改这个最重要的,就是别烦,其实你慢慢捋顺了就好了。

小程序之 webview 在 ios 下访问不到的坑

ios 端小程序有的页面白屏,后来查到的问题是 web-view 的 src 中携带的参数中含有中文,ios 端是不允许链接中有中文的,所以只能给中文转码了,h5 页面提取参数的时候再解码一下就可以了

1 中文转码

1
encodeURI(url);

2 解码

1
decodeURI(url);

还有一些关于 webview 的知识点:

- 每个页面只能有一个会自动铺满整个页面,并覆盖其他组件,小程序对 webview 的监控状态基本没有,只能设置 src 设置 url。
- 关于小程序和 web-view 的通信, → 小程序只能通过 JSSDK 1.3.0 提供的接口返回小程序页面,设置参数来传值,反之,小程序到 webview 也是一样的,只能是 src 的路径带上参数;
- web-view 不支持支付能力,是指无法唤起小程序的直接支付窗口,对于 h5 的那套支付应该是支持的,但是 web-view 里边没法使用 微信支付的 JSAPI,也就是可能可以 h5 的相关的的支付中心来支付;
- 关于层级,在 webview 中可以无限跳转,对于导航条返回和物理键返回都会回到上一个页面直到退出 webview,就像 history.back。
- webview 中的 html 的 title 会自动放到小程序的头部作为标题;

小程序之调用上个页面方法

因为,现在的项目是小程序嵌套 h5,金融类目,做人脸识别 在小程序,然后购买理财、存款之类的在 h5 里面,识别之后要根据原来的路径就回到原来的 h5 页面。记得官方文档上面说过 getCurrentPages() 不要改变路由,但是也能获取到页面的方法,于是就可以实现我的需求喽。代码如下:

1
2
3
4
5
6
7
8
9
let pages = getCurrentPages();
let lastpage = pages[pages.length - 2];
if (lastpage.route === 'pages/webview/webview') {
lastpage.changeUrl();
setTimeout(() => {
clearInterval(this.data.time);
wx.navigateBack({});
}, 500);
}

然后我只需要在我 webview 的 js 里面写更换去 h5 的路由:

1
2
3
4
5
6
7
8
9
10
11
12
13
changeUrl() {
if (wx.getStorageSync('faceSucc')) {
let url = app.config.h5url + "/" + Util.formatParm(app.globalData.face_retCode, app.parm.h5_servid_url); // console.log(app.globalData.header) // url = url + "?param=" + encodeURIComponent(JSON.stringify(app.globalData.header)) // 缓存bug
if (url.indexOf("?") !== -1) {
url = url + "&d=" + new Date().getTime()
} else {
url = url + "?d=" + new Date().getTime()
}
this.setData({
url: url
})
}
},

就可以在 back 的时候 已经修改了 上个页面的 h5 的路由。是不是很方便呢。

小程序之使用 AES 加密和 NPM

项目需要 aes 加密,找了一下前端可以用 crypto-js 来做。有人写好的 js 文件之类的,粘贴过来就可以准备开发了。但是想起了我龙哥的话,写代码要优雅。正好也一直关注着微信小程序生态,知道可以使用 npm 了。正好有 crypto-js 的包,说干就干。首先在小程序的项目目录里面 执行 npm init –yes, 生成一个 package.json。还有官方文档的建议,下载 npm 包的时候 执行 npm install –production。npm install –production –save crypto-js。这样咱们想要的就都有了,主要说一下 aes 加密,啦啦啦。首先先引入嘛


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/** * aes加密 * {加密字段} word * {秘钥} keyStr */
function encrypt_aes(word, keyStr) {
let key = CryptoJS.enc.Utf8.parse(keyStr);
let srcs = CryptoJS.enc.Utf8.parse(word);
let encrypted = CryptoJS.AES.encrypt(srcs, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
let result = encrypted.toString();
console.log("aes加密字段", result)
return result;
}
/** * aes解密 * {加密字段} word * {秘钥} keyStr */
function decrypt_aes(word, keyStr) {
let key = CryptoJS.enc.Utf8.parse(keyStr);
let decrypt = CryptoJS.AES.decrypt(word, key, {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7
});
let result = CryptoJS.enc.Utf8.stringify(decrypt).toString();
console.log("aes解密字段", result)
return result;
}

效果如图:

这样就可以实现 前端aes加密了。可以送我一朵小发发~

一步步记录自己的踩坑历程~我要做到我技术不是最好的,但我给你总结的小程序的东西是最简单粗暴的哈哈哈

本文标题:送给写小程序的你。

文章作者:Jonathon

发布时间:2018年07月05日 - 20:07

最后更新:2019年07月08日 - 10:07

原始链接:https://www.jonathon.cn/送给写小程序的你.html

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

苟富贵,勿相忘!