开发就是一个西行取经的过程,期间不是一路顺风,不定会遇到什么鬼。javascript
最近开发一个简单的每日答题签到小程序,期间遇到一些始料未及的坑记录一下,避免往后重复踩坑。本文针对初入小程序的小白。:)css
可能会有更好的解决方案,欢迎指正。java
多张gif图预警,大概20M 😱git
想要实现一个横向滚动的日历,很天然的想到scroll-view组件,因而洋洋洒洒的写下几行代码:github
demo.wxmlweb
<scroll-view scroll-x="true" class="scroll-view-demo" > <view class="item" wx:for="{{[1,2,3,4,5,6,7,8,9,10,11,12,13,14]}}" wx:key="{{index}}"> {{item}} </view> </scroll-view> 复制代码
demo.scss(为了写着方便,请自行转换成wxss语法,下同)json
page { width: 100%; height: 100%; background: #17448E; } .scroll-view-demo { display: flex; flex-direction: row; .item { width: 80rpx; height: 80rpx; border: solid 1px #fff; display: flex; color: #fff; font-size: 36rpx; justify-content: center; align-items: center; border-radius: 40rpx; margin-left: 50rpx; } } 复制代码
搓搓手,看看效果小程序
尴尬,说好的横向滚动呢,明明flex-direction: row;
写的清清楚楚明明白白。。segmentfault
既然scroll-view
对flex
不友好,那我在子元素上再包裹一层view
组件应该万事大吉了吧,因而:微信小程序
demo.wxml
<scroll-view scroll-x="true" class="scroll-view-demo" > <view class="item-container"> <view class="item" wx:for="{{[1,2,3,4,5,6,7,8,9,10,11,12,13,14]}}" wx:key="{{index}}"> {{item}} </view> </view> </scroll-view> 复制代码
demo.scss
.scroll-view-demo { .item-container { display: flex; flex-direction: row; .item { width: 80rpx; height: 80rpx; border: solid 1px #fff; display: flex; color: #fff; font-size: 36rpx; justify-content: center; align-items: center; border-radius: 40rpx; margin-left: 50rpx; } } } 复制代码
看看效果:
然鹅。。好像不是那么回事,子元素怎么就被挤成胶囊了。。 仔细研究一番,须要给.item-container
加上固定的width
才行,甚是麻烦。
简单点,开发的方式简单点,既然scroll-view
对flex
如此高冷,那就换一种方式:
demo.wxml
<scroll-view scroll-x="true" class="scroll-view-demo" > <view class="item" wx:for="{{[1,2,3,4,5,6,7,8,9,10,11,12,13,14]}}" wx:key="{{index}}"> {{item}} </view> </scroll-view> 复制代码
demo.scss
.scroll-view-demo { width: 100%; white-space: nowrap; .item { width: 80rpx; height: 80rpx; line-height: 80rpx; text-align: center; border: solid 1px #fff; display: inline-block; color: #fff; font-size: 36rpx; border-radius: 40rpx; margin-left: 50rpx; } 复制代码
效果:
两个字,接近完美😆
不过,在手机上浏览scroll-view
会有一个滚动条,很丑有木有
因而挑灯夜读翻阅资料找到解决方法:
app.css
/*隐藏scroll-view滚动条*/
::-webkit-scrollbar{
width: 0;
height: 0;
color: transparent;
}
复制代码
这下妥妥的了,
年轻人切记,
scroll-view
尽可能不用flex
觉得scroll-view
踩完一个坑就结束了么,图森破图样。
想给scroll-view
在页面loading结束后滚动到某一个位置, 很简单,闭着眼写代码:
demo.wxml
<scroll-view scroll-x="true" class="daily-check-calendar-scroll" scroll-left="{{scrollLeft}}" > <view class="item" wx:for="{{list}}" wx:key="{{index}}"> {{item}} </view> </scroll-view> 复制代码
demo.js
Page({ data: { scrollLeft: 100, list: [] }, onLoad(){ setTimeout(()=>{ //伪装异步获取数据 this.setData({ list: [1,2,3,4,5,6,7,8] }); },1000); } }) 复制代码
看效果,发现scroll-view
带在原地纹丝不动。惆怅~
let me see see 代码稍做改动:
demo.js
Page({ data: { scrollLeft: 0, list: [] }, onLoad(){ setTimeout(()=>{ //伪装异步获取数据 this.setData({ list: [1,2,3,4,5,6,7,8], scrollLeft: 100 }); },1000); } }) 复制代码
perfect 果真达到了预期,细想缘由:
data
中设置的scroll-left
值会在数据渲染前赋值.scroll-view
中仍是空的,因此scroll-left
不会生效. 咱们应该在数据渲染到scroll-view
中之后,再同步scroll-left
的值想写一个自定义样式的按钮,so easy,给我一秒写出来:
demo.wxml
<view class="button">超新脱俗的按钮</view> <button>超新脱俗的按钮,too</button> 复制代码
demo.scss
button, .button { width: 500rpx; height: 88rpx; display: flex; align-items: center; justify-content: center; font-size: 32rpx; background: #FE7437; color: #fff; border-radius: 44rpx; margin: 20rpx auto; } 复制代码
效果:
猛一看,感受不能再完美了,定睛一看,怎么以为button
组件写出来的按钮不是那么纯洁,有阴影。
很天然的再加行代码:
demo.scss
border: none;
复制代码
事情并无那么简单,阴影依然噩梦般存在。 有困难,找百度,果不其然大神们的奇技淫巧顺利解决,贴上完整代码:
demo.scss
button, .button { width: 500rpx; height: 88rpx; display: flex; align-items: center; justify-content: center; font-size: 32rpx; background: #FE7437; color: #fff; border-radius: 44rpx; margin: 20rpx auto; &::after{ border-radius: 0; border: none; } } 复制代码
效果:
简直两个按钮如出一辙。
小程序开发免不了要有登陆页面,因而就离不开input
组件,代码就不贴了,常规操做,直接上图。
看图很容易发现几个问题:
input
时 文字会闪动input
时 须要屡次才能获取到焦点,弹出键盘。针对第一个问题,百度谷歌挖地三尺也木有找到好的解决办法(多是挖的不够深 :() 第二个问题,既然不能自主获取焦点,那咱们结合官方文档助它一臂之力:
demo.wxml
<view class="account-main"> <text class="account-title">登陆</text> <form class="login-form" bindsubmit="formSubmit"> <view class="form-item {{selectName ? 'selected' : ''}}"> <input type="text" value="{{username}}" focus="{{selectName}}" bindtap="handleName" bindinput="getUsername" placeholder-class="login-ipt-place" class="login-ipt" placeholder="手机号/邮箱" /> </view> <view class="form-item {{selectPwd ? 'selected' : ''}}"> <input type="text" value="{{password}}" focus="{{selectPwd}}" bindtap="handlePwd" bindinput='getPassword' placeholder-class="login-ipt-place" maxlength="48" class="login-ipt pwd-ipt" placeholder="登陆密码" password="true" /> </view> <button class="form-submit" form-type="submit"> 登陆 </button> </form> </view> 复制代码
demo.scss
...
复制代码
demo.js
Page({ data: { username: '', password: '', selectName: true, selectPwd: false, }, getUsername(e){ //获取用户名 this.setData({ username: e.detail.value, }); }, getPassword(e){ //获取密码 this.setData({ password: e.detail.value, }) }, handleName(){ this.setData({ selectName: true, selectPwd: false, }) }, handlePwd(){ this.setData({ selectName: false, selectPwd: true, }) } }); 复制代码
看看效果:
看着像是好多了 😆 思考: 若文本框不止两个,怎么写才更优雅,另,没有看在不一样手机上的表现。
第三个关于密码黑点大小的问题 我也木有良策 +_+
如下是网上大佬们关于input
组件bug的总结:
placeholder
文字与 input
的值重叠 暂无解决方法
获取焦点 和 失去焦点 时,光标和文字跳动 暂无解决方法
当 input
设置为居中对齐时,光标会出如今奇怪的位置 暂无解决方法
bindconfirm
事件在失去焦点时也会触发,相似于 blur
暂无解决方法
对 input
作动画时,若是是获取焦点状态,会失效 暂无解决方案,由于 input 在获取焦点时是native 组件,失去焦点后改回 web 组件
type
为 idcard
, digit
时并非调用数字键盘 暂无解决方案,目前起做用的只有 number
在input
聚焦期间,不能作css
动画,不然input
中的placeholder
会错位,若是动画和聚焦都想要的话,那么能够在动画完成以后,再设置聚焦
看看微信官方社区的吐槽,若是你也被坑了,去给助个攻吧
小程序登陆时咱们能够利用微信获取手机号快捷登陆,根据文档咱们很容易写一套登陆流程:
demo.wmxl
<button class="login-by-wx" open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber"> 微信登陆 </button> 复制代码
demo.js
Page({ ... async getPhoneNumber(e) { if(!e.detail.iv){ //被拒绝受权 return; } let code = ''; /* * 别慌,wxPromise.login()其实就是wx的api被promise化,参考 **转转**代码 * https://github.com/zhuanzhuanfe/fancy-mini/blob/master/src/wxPromise.js */ const res = await wxPromise.login(); code = res.code; if(!code){ console.error(`调用 wx.login 失败`, err); return util.toast('微信登陆失败'); } try{ //server端微信登陆接口 const result = await service.passport.wxLogin({ data: { code: code, iv: e.detail.iv, encryptedData: e.detail.encryptedData } }); const res = result.data; if (res.status === 0) { // 登陆成功 ... }else { ... } }catch(err){ ... } } ... }); 复制代码
server.js
async wxLoginAction(){ //微信登陆 const {ctx} = this; const body = ctx.request.body; //请求微信接口 https://api.weixin.qq.com/sns/jscode2session const result = await this.callService('wxApplet.code2Session', { appid: ***, secret: ***, js_code: body.code, grant_type: 'authorization_code' }); if(result.status == 0){ let sessionKey = result.data.session_key; let encryptedData = body.encryptedData; let iv = body.iv; //解密获取手机号 (解密库小程序文档有对应的源码) let pc = new WXBizDataCrypt(appid, sessionKey); let mobile = pc.decryptData(encryptedData, iv).phoneNumber; //解密成功 生成登陆态等操做 ... return this.json({ status: 0, message: 'ok', data: mobile }); }else { .... } } 复制代码
搓搓手,看效果:
居然首次登陆的时候有报错,之后再次登陆却一切正常。
看server端报错日志,以下:
Error: error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt
复制代码
这条error
信息是从WXBizDataCrypt
解密失败时抛出来的。
查看文档,看button的官方文档Tips中有这一条:
在bindgetphonenumber 等返回加密信息的回调中调用 wx.login 登陆,可能会刷新登陆态。此时服务器使用 code 换取的 sessionKey 不是加密时使用的 sessionKey,致使解密失败。建议开发者提早进行 login;或者在回调中先使用 checkSession 进行登陆态检查,避免 login 刷新登陆态。
另外,查看网上大神的解决方案,其中有一条是这样说的:
纳尼,点击按钮以前就要调用login
,因而将信将疑的再次修改代码:
demo.js
Page({ data: { code: '' }, async onLoad(options){ try { const res = await wxPromise.login(); this.setData({ code: res.code }) }catch (err){ this.setData({ code: '' }) } }, async getPhoneNumber(e) { if(!e.detail.iv){ //被拒绝受权 return; } let code = this.data.code; if(!code){ console.error(`调用 wx.login 失败`, err); return util.toast('微信登陆失败'); } try{ //server端微信登陆接口 const result = await service.passport.wxLogin({ data: { code: code, iv: e.detail.iv, encryptedData: e.detail.encryptedData } }); const res = result.data; if (res.status === 0) { // 登陆成功 ... }else { ... } }catch(err){ ... } } }); 复制代码
修改完毕,通过测试,首次登陆果真很顺利就成功了,可是又出现了一个幺蛾子,再次登陆就又又又报错:
error: code is used
复制代码
这个报错就很好理解了,稍稍修改一下代码:
demo.js
Page({ data: { code: '' }, async onLoad(options){ try { const res = await wxPromise.login(); this.setData({ code: res.code }) }catch (err){ this.setData({ code: '' }) } }, async getPhoneNumber(e) { if(!e.detail.iv){ //被拒绝受权 return; } let code = this.data.code; if (!code) { //防止code失效 try{ const res = await wxPromise.login(); code = res.code; }catch(err){ console.error(`调用 wx.login 失败`, err); return util.toast('微信登陆失败'); } } try{ //server端微信登陆接口 const result = await service.passport.wxLogin({ data: { code: code, iv: e.detail.iv, encryptedData: e.detail.encryptedData } }); const res = result.data; this.setData({ //用过的code须要清空,从新获取 code: '' }); if (res.status === 0) { // 登陆成功 ... }else { ... } }catch(err){ ... } } }); 复制代码
泪流满面,自我测试,确实经得起组织考验,完美解决。(也可能测试的不够透彻,若是还有报错,或者更好的解决方案,欢迎打脸T T)
image
组件开发时特别常见,要写一个宽度固定,高度自适应代码也很简单:
demo.wxml
<image mode = "widthFix" src="你的图片地址" > </image> 复制代码
看着很简单,感受稳稳地,可是刷新页面时会有中见鬼的赶脚,话很少说上图:
仔细看彷佛有鬼影掠过。。。 加载时图片被拉扯变形,😱
其实解决方法也很简单:
demo.scss
image {
height: auto;
}
复制代码
对,就是这么简单,就完美解决。😆
以上。
开发是一个漫长的过程,前面未知的坑还不少。火来水挡水来土掩,填的坑越多,路会越平坦,再攒一波坑,等下次更新。
最后插播一条广告,通过一天屡次被拒后,小程序终于审核经过了(感谢各类TV T T),欢迎体验吐槽
参考文章:
小程序里的登陆态处理,主要参考如下文章(具体实现省略):