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