微信小程序踩坑指南

微信小程序降(小)妖伏(小)魔篇

開發就是一個西行取經的過程,期間不是一路順風,不定會遇到什麼鬼。javascript

最近開發一個簡單的每日答題簽到小程序,期間遇到一些始料未及的坑記錄一下,避免往後重複踩坑。本文針對初入小程序的小白。:)css

可能會有更好的解決方案,歡迎指正。java

多張gif圖預警,大概20M 😱git

第一難: scroll-view標籤對dispay:flex無效

想要實現一個橫向滾動的日曆,很天然的想到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;
  }
}
複製代碼

搓搓手,看看效果小程序

scroll-view

尷尬,說好的橫向滾動呢,明明flex-direction: row;寫的清清楚楚明明白白。。segmentfault

既然scroll-viewflex不友好,那我在子元素上再包裹一層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;
    }
  }
}
複製代碼

看看效果:

scroll-view-flex

然鵝。。好像不是那麼回事,子元素怎麼就被擠成膠囊了。。 仔細研究一番,須要給.item-container加上固定的width才行,甚是麻煩。

簡單點,開發的方式簡單點,既然scroll-viewflex如此高冷,那就換一種方式:

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-ok

兩個字,接近完美😆

不過,在手機上瀏覽scroll-view會有一個滾動條,很醜有木有

因而挑燈夜讀翻閱資料找到解決方法:

app.css

/*隱藏scroll-view滾動條*/
::-webkit-scrollbar{
  width: 0;
  height: 0;
  color: transparent;
}
複製代碼

這下妥妥的了,

年輕人切記,scroll-view 儘可能不用flex

第二難: scroll-view 設置scroll-left無效問題

覺得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的值

第三難: button自定義樣式,沒法去掉默認樣式

想寫一個自定義樣式的按鈕,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

猛一看,感受不能再完美了,定睛一看,怎麼以爲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;
  }
}
複製代碼

效果:

button-ok

簡直兩個按鈕如出一轍。

第四難 讓人傷神的input組件

小程序開發免不了要有登陸頁面,因而就離不開input組件,代碼就不貼了,常規操做,直接上圖。

input-error

看圖很容易發現幾個問題:

  1. 切換input時 文字會閃動
  2. 切換下一個input時 須要屢次才能獲取到焦點,彈出鍵盤。
  3. 若是在不一樣的手機上看,會發現密碼框的黑點大小也不一致

針對第一個問題,百度谷歌挖地三尺也木有找到好的解決辦法(多是挖的不夠深 :() 第二個問題,既然不能自主獲取焦點,那咱們結合官方文檔助它一臂之力:

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-ok

看着像是好多了 😆 思考: 若文本框不止兩個,怎麼寫才更優雅,另,沒有看在不一樣手機上的表現。

第三個關於密碼黑點大小的問題 我也木有良策 +_+

如下是網上大佬們關於input組件bug的總結:

  • placeholder 文字與 input 的值重疊 暫無解決方法

  • 獲取焦點 和 失去焦點 時,光標和文字跳動 暫無解決方法

  • input 設置爲居中對齊時,光標會出如今奇怪的位置 暫無解決方法

  • bindconfirm 事件在失去焦點時也會觸發,相似於 blur 暫無解決方法

  • input 作動畫時,若是是獲取焦點狀態,會失效 暫無解決方案,由於 input 在獲取焦點時是native 組件,失去焦點後改回 web 組件

  • typeidcard, digit 時並非調用數字鍵盤 暫無解決方案,目前起做用的只有 number

  • input聚焦期間,不能作css動畫,不然input中的placeholder會錯位,若是動畫和聚焦都想要的話,那麼能夠在動畫完成以後,再設置聚焦

看看微信官方社區的吐槽,若是你也被坑了,去給助個攻吧

input

傳送門

第五難 getPhoneNumber獲取用戶手機號解密報錯問題

小程序登陸時咱們能夠利用微信獲取手機號快捷登陸,根據文檔咱們很容易寫一套登陸流程:

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 {
        	....
        }
}
複製代碼

搓搓手,看效果:

login

居然首次登陸的時候有報錯,之後再次登陸卻一切正常。

看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-error

納尼,點擊按鈕以前就要調用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>
複製代碼

看着很簡單,感受穩穩地,可是刷新頁面時會有中見鬼的趕腳,話很少說上圖:

image

仔細看彷佛有鬼影掠過。。。 加載時圖片被拉扯變形,😱

其實解決方法也很簡單:

demo.scss

image {
	height: auto;
}
複製代碼

對,就是這麼簡單,就完美解決。😆

以上。

結束

開發是一個漫長的過程,前面未知的坑還不少。火來水擋水來土掩,填的坑越多,路會越平坦,再攢一波坑,等下次更新。

最後插播一條廣告,通過一天屢次被拒後,小程序終於審覈經過了(感謝各類TV T T),歡迎體驗吐槽


參考文章:

小程序裏的登陸態處理,主要參考如下文章(具體實現省略):

相關文章
相關標籤/搜索