開發就是一個西行取經的過程,期間不是一路順風,不定會遇到什麼鬼。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),歡迎體驗吐槽
參考文章:
小程序裏的登陸態處理,主要參考如下文章(具體實現省略):