上班實(shí)習(xí)的第一天,接到任務(wù),要求開(kāi)發(fā)一個(gè)微信小程序。小程序名為【翼簽到】,用作公司內(nèi)部發(fā)布大會(huì)消息和報(bào)名簽到。
接到設(shè)計(jì)稿,估摸了一下,發(fā)現(xiàn)頁(yè)面比較多,還好導(dǎo)師給我安排一個(gè)小伙伴進(jìn)行協(xié)作開(kāi)發(fā)。他主要負(fù)責(zé)發(fā)布流程而我主要負(fù)責(zé)著陸流程、審批流程和后臺(tái)接口的開(kāi)發(fā)。

微信小程序
開(kāi)發(fā)微信小程序是一件很有意思的事情。原因有二:
- 簡(jiǎn)潔易懂的中文文檔
- 所見(jiàn)即所得的開(kāi)發(fā)快感
入門(mén)開(kāi)發(fā)小程序的成本是相當(dāng)?shù)偷?,但是想要?xiě)好來(lái),也是一件不簡(jiǎn)單的事情,也踩過(guò)了一些坑,在這里和大家分享一下。
1.setData()
調(diào)用setData()改變data中的數(shù)據(jù),并不是實(shí)時(shí)的。頻繁的(毫秒級(jí))調(diào)用setData()會(huì)嚴(yán)重影響頁(yè)面性能。至于原理,微信小程序的文檔說(shuō)得很清楚了。
https://developers.weixin.qq.com/miniprogram/dev/framework/performance/tips.html?search-key=%E6%80%A7%E8%83%BD
2.生命周期函數(shù)onShow
官方文檔給這個(gè)聲明周期函數(shù)給出的介紹是:onShow: 頁(yè)面顯示,每次打開(kāi)頁(yè)面都會(huì)調(diào)用一次。當(dāng)我們調(diào)用微信小程序API路由跳轉(zhuǎn)至一個(gè)頁(yè)面的時(shí)候,該頁(yè)面掛載的onShow函數(shù),往往都會(huì)執(zhí)行一次。
3.在視圖容器swiper中使用map組件
<swiper>
<swiper-item>大會(huì)介紹</swiper-item>
<swiper-item>大會(huì)議程</swiper-item>
<swiper-item>出席嘉賓</swiper-item>
<swiper-item>地圖定位
<map> </map>
</swiper-item>
</swiper>
官方文檔明確指出<map>組件不能嵌套在<swiper>組件當(dāng)中使用。上面的代碼違反了這條規(guī)定。出現(xiàn)的結(jié)果就是:在微信開(kāi)發(fā)者工具中的模擬器測(cè)試,四個(gè)<swiper-item>能夠流暢切換,表現(xiàn)正常。但是,當(dāng)在手機(jī)上進(jìn)行測(cè)試的時(shí)候,便出現(xiàn)了<map>組件停留在原地,不會(huì)隨著父元素的<swiper-item>滑動(dòng)的問(wèn)題。
對(duì)于這樣的問(wèn)題,目前的解決方案是:使用<image>組件替換<map>組件,也就是展示一張靜態(tài)的假地圖。當(dāng)用戶(hù)需要操作地圖的時(shí)候,<image>組件隱藏,將真正需要展示<map>組件顯示出來(lái)即可。
4.微信小程序啟動(dòng)機(jī)制
微信小程序只有冷啟動(dòng)和熱啟動(dòng)之分。當(dāng)用戶(hù)打開(kāi)過(guò)某個(gè)小程序,然后在一定時(shí)間內(nèi)(目前為5分鐘)再次打開(kāi),只需將后臺(tái)態(tài)的小程序切換到前臺(tái),這是熱啟動(dòng)。冷啟動(dòng)指的是用戶(hù)首次打開(kāi)或小程序被微信主動(dòng)銷(xiāo)毀后再次打開(kāi)的情況,此時(shí)小程序需要重新加載啟動(dòng)。
微信小程序被銷(xiāo)毀的情況有兩種:
1.當(dāng)小程序進(jìn)入后臺(tái),客戶(hù)端會(huì)維持一段時(shí)間的運(yùn)行狀態(tài),超過(guò)一定時(shí)間后(目前是5分鐘)會(huì)被微信主動(dòng)銷(xiāo)毀
2.當(dāng)短時(shí)間內(nèi)(5s)連續(xù)收到兩次以上收到系統(tǒng)內(nèi)存告警,會(huì)進(jìn)行小程序的銷(xiāo)毀
5.密切關(guān)注微信小程序API的變動(dòng)
作為一個(gè)系統(tǒng)的開(kāi)發(fā)者和維護(hù)者,要關(guān)注官方文檔中API的變動(dòng),因?yàn)槟承〢PI的變動(dòng)可能會(huì)影響到使用該API的模塊功能,因此需要做出及時(shí)的調(diào)整。例如,在今年4月份的時(shí)候,微信小程序變動(dòng)了獲取用戶(hù)信息的接口(wx.getUserInfo),具體見(jiàn)下圖:

簡(jiǎn)單來(lái)說(shuō),此次更新,是微信小程序獲取用戶(hù)信息這一操作更加傾向于用戶(hù)主動(dòng)提交個(gè)人信息。如果程序開(kāi)發(fā)者不及時(shí)更新維護(hù)的話(huà),那么原先調(diào)用wx.getUserInfo這一接口,將會(huì)報(bào)錯(cuò)。
在這種情況下,拿【有贊服務(wù)指南小程序】舉例,該小程序在獲取用戶(hù)頭像的時(shí)候(用戶(hù)信息的一部分),明顯的修改為“點(diǎn)擊獲取頭像”,指引用戶(hù)主動(dòng)點(diǎn)擊提交個(gè)人信息以獲取個(gè)人頭像信息。就算用戶(hù)出于安全考慮不點(diǎn)擊,那么小程序也不會(huì)調(diào)用wx.getUserInfo接口,因此,也不會(huì)產(chǎn)生錯(cuò)誤。

后臺(tái)技術(shù)棧
依照公司內(nèi)部的技術(shù)棧體系,后臺(tái)技術(shù)棧選用了Node+Express4.0+MySQL。作為一個(gè)已經(jīng)有Koa框架開(kāi)發(fā)經(jīng)驗(yàn)的我來(lái)說(shuō),使用Express開(kāi)發(fā)后臺(tái)接口的難度并不高。整個(gè)開(kāi)發(fā)周期,花費(fèi)在前端開(kāi)發(fā)的時(shí)間遠(yuǎn)比開(kāi)發(fā)后臺(tái)接口所花的時(shí)間多得多。
其它
和我搭檔的小伙伴,在發(fā)布流程開(kāi)發(fā)至一半的時(shí)候,離職返校答辯去了,留下我孤零零的攬下整個(gè)小程序的開(kāi)發(fā)。于是,在他編寫(xiě)的代碼基礎(chǔ)上,我繼續(xù)對(duì)發(fā)布流程進(jìn)行開(kāi)發(fā),讓我有了不小的收獲。
1.一個(gè)函數(shù)只做一件事情
下面代碼的封裝了一個(gè)getConferenceList()方法,由其函數(shù)名可以知道這個(gè)函數(shù)的大概作用——獲取大會(huì)列表。但是實(shí)際上,這個(gè)函數(shù)中做了太多的事情,除了獲取大會(huì)列表之外,調(diào)用了showLoading()展示loading動(dòng)畫(huà),調(diào)用了stopPullDownRefresh()取消下載刷新,調(diào)用了showModal()彈出模態(tài)框,最后還調(diào)用了setData()修改了數(shù)據(jù)層...
如此雜亂的函數(shù),復(fù)用率和自由度都大打折扣!
getConferenceList: function (data, sessionId, that) {
wx.showLoading({ title: '加載中', mask: true });
api.conferenceList(data, sessionId).then((res) => {
if (res.data && res.data.code === 1) {
let conferenceList = res.data.conferenceList;
that.setData({ conferenceList }, function () {
wx.stopPullDownRefresh();
wx.hideLoading();
});
} else {
wx.hideLoading();
wx.showModal({ title: '服務(wù)器開(kāi)小差啦', content: '請(qǐng)聯(lián)系管理員或稍后重試' })
}
}, err => {
wx.hideLoading();
wx.showModal({ title: '與服務(wù)器通訊錯(cuò)誤', content: '請(qǐng)聯(lián)系管理員或稍后重試' })
});
}
下拉刷新,調(diào)用getConferenceList()方法的時(shí)候,弊病就暴露出來(lái)了:
1.不能夠的控制調(diào)用setData()和stopPullDownRefresh()的時(shí)機(jī),因?yàn)間etConferenceList()內(nèi)部已經(jīng)調(diào)用了。
2.下拉刷新自帶loading動(dòng)畫(huà),不需要調(diào)用showLoading()方法,getConferenceList()內(nèi)部調(diào)用showLoading()方法很多余。
onPullDownRefresh: function () {
let that = this;
that.setData({ page: 1, conferenceList: [], finish: false }, function () {
that.getConferenceList({ page: that.data.page, size: that.data.size }, that.data.sessionId, that);
});
}
正因?yàn)槿绱?,所以才明白?strong>“一個(gè)函數(shù)只做一件事情”的重要性了。
嘗試對(duì)getConferenceList()方法進(jìn)行改造,去掉多余的操作,讓其只作為獲取大會(huì)列表信息的一個(gè)封裝 。改造后的代碼如下。
getConferenceList: function (data, sessionId) {
return api.conferenceList(data, sessionId).then((res) => {
if (res.data && res.data.code === 1) {
let conferenceList = res.data.conferenceList;
return conferenceList;
} else {
wx.showToast({ title: '服務(wù)端異常', mask: true, icon: 'none', duration: 500 });
}
}, err => {
wx.showToast({ title: '服務(wù)器連接失敗', mask: true, icon: 'none', duration: 500 });
});
}
改造后的getConferenceList()方法內(nèi)部返回一個(gè)promise對(duì)象,將獲取大會(huì)信息列表之后的控制權(quán)交至消費(fèi)者的手中,而本身只做一件事,獲取大會(huì)消息列表。
這樣一來(lái),便可以控制調(diào)用setData()和stopPullDownRefresh()的時(shí)機(jī),靈活度更高。
onPullDownRefresh: function () {
let that = this;
that.getConferenceList({ page: 1, size: that.data.size }, that.data.sessionId).then((value) => {
that.setData({ page: 1, conferenceList: value }, function () {
wx.stopPullDownRefresh()
});
});
}
2.合理控制數(shù)據(jù)流
一個(gè)合格的前端開(kāi)發(fā)工程師一定是善于控制數(shù)據(jù)流的,合理的控制數(shù)據(jù)流首先需要明確每個(gè)數(shù)據(jù)的生命周期,是伴隨整個(gè)小程序的生命周期還是執(zhí)行完某個(gè)函數(shù)就銷(xiāo)毀了。確定了生命周期再選擇存儲(chǔ)數(shù)據(jù)的方式,是存儲(chǔ)在localStorage、sessionStorage還是掛載到全局對(duì)象又或者僅僅需要路由傳值,這些都需要根據(jù)數(shù)據(jù)的生命周期去考量。
喜歡這篇文章的朋友們,請(qǐng)點(diǎn)個(gè)贊再走哦~