上班实习的第一天,接到任务,要求开发一个微信小程序。小程序名为【翼签到】,用作公司内部发布大会消息和报名签到。
接到设计稿,估摸了一下,发现页面比较多,还好导师给我安排一个小伙伴进行协作开发。他主要负责发布流程而我主要负责着陆流程、审批流程和后台接口的开发。
微信小程序
开发微信小程序是一件很有意思的事情。原因有二:
- 简洁易懂的中文文档
- 所见即所得的开发快感
入门开发小程序的成本是相当低的,但是想要写好来,也是一件不简单的事情,也踩过了一些坑,在这里和大家分享一下。
1.setData()
调用setData()改变data中的数据,并不是实时的。频繁的(毫秒级)调用setData()会严重影响页面性能。至于原理,微信小程序的文档说得很清楚了。
https://developers.weixin.qq.com/miniprogram/dev/framework/performance/tips.html?search-key=%E6%80%A7%E8%83%BD
2.生命周期函数onShow
官方文档给这个声明周期函数给出的介绍是:onShow: 页面显示,每次打开页面都会调用一次。当我们调用微信小程序API路由跳转至一个页面的时候,该页面挂载的onShow函数,往往都会执行一次。
3.在视图容器swiper中使用map组件
<swiper>
<swiper-item>大会介绍</swiper-item>
<swiper-item>大会议程</swiper-item>
<swiper-item>出席嘉宾</swiper-item>
<swiper-item>地图定位
<map> </map>
</swiper-item>
</swiper>
官方文档明确指出<map>组件不能嵌套在<swiper>组件当中使用。上面的代码违反了这条规定。出现的结果就是:在微信开发者工具中的模拟器测试,四个<swiper-item>能够流畅切换,表现正常。但是,当在手机上进行测试的时候,便出现了<map>组件停留在原地,不会随着父元素的<swiper-item>滑动的问题。
对于这样的问题,目前的解决方案是:使用<image>组件替换<map>组件,也就是展示一张静态的假地图。当用户需要操作地图的时候,<image>组件隐藏,将真正需要展示<map>组件显示出来即可。
4.微信小程序启动机制
微信小程序只有冷启动和热启动之分。当用户打开过某个小程序,然后在一定时间内(目前为5分钟)再次打开,只需将后台态的小程序切换到前台,这是热启动。冷启动指的是用户首次打开或小程序被微信主动销毁后再次打开的情况,此时小程序需要重新加载启动。
微信小程序被销毁的情况有两种:
1.当小程序进入后台,客户端会维持一段时间的运行状态,超过一定时间后(目前是5分钟)会被微信主动销毁
2.当短时间内(5s)连续收到两次以上收到系统内存告警,会进行小程序的销毁
5.密切关注微信小程序API的变动
作为一个系统的开发者和维护者,要关注官方文档中API的变动,因为某些API的变动可能会影响到使用该API的模块功能,因此需要做出及时的调整。例如,在今年4月份的时候,微信小程序变动了获取用户信息的接口(wx.getUserInfo),具体见下图:
简单来说,此次更新,是微信小程序获取用户信息这一操作更加倾向于用户主动提交个人信息。如果程序开发者不及时更新维护的话,那么原先调用wx.getUserInfo这一接口,将会报错。
在这种情况下,拿【有赞服务指南小程序】举例,该小程序在获取用户头像的时候(用户信息的一部分),明显的修改为“点击获取头像”,指引用户主动点击提交个人信息以获取个人头像信息。就算用户出于安全考虑不点击,那么小程序也不会调用wx.getUserInfo接口,因此,也不会产生错误。
后台技术栈
依照公司内部的技术栈体系,后台技术栈选用了Node+Express4.0+MySQL。作为一个已经有Koa框架开发经验的我来说,使用Express开发后台接口的难度并不高。整个开发周期,花费在前端开发的时间远比开发后台接口所花的时间多得多。
其它
和我搭档的小伙伴,在发布流程开发至一半的时候,离职返校答辩去了,留下我孤零零的揽下整个小程序的开发。于是,在他编写的代码基础上,我继续对发布流程进行开发,让我有了不小的收获。
1.一个函数只做一件事情
下面代码的封装了一个getConferenceList()方法,由其函数名可以知道这个函数的大概作用——获取大会列表。但是实际上,这个函数中做了太多的事情,除了获取大会列表之外,调用了showLoading()展示loading动画,调用了stopPullDownRefresh()取消下载刷新,调用了showModal()弹出模态框,最后还调用了setData()修改了数据层...
如此杂乱的函数,复用率和自由度都大打折扣!
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: '服务器开小差啦', content: '请联系管理员或稍后重试' })
}
}, err => {
wx.hideLoading();
wx.showModal({ title: '与服务器通讯错误', content: '请联系管理员或稍后重试' })
});
}
下拉刷新,调用getConferenceList()方法的时候,弊病就暴露出来了:
1.不能够的控制调用setData()和stopPullDownRefresh()的时机,因为getConferenceList()内部已经调用了。
2.下拉刷新自带loading动画,不需要调用showLoading()方法,getConferenceList()内部调用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);
});
}
正因为如此,所以才明白了“一个函数只做一件事情”的重要性了。
尝试对getConferenceList()方法进行改造,去掉多余的操作,让其只作为获取大会列表信息的一个封装 。改造后的代码如下。
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: '服务端异常', mask: true, icon: 'none', duration: 500 });
}
}, err => {
wx.showToast({ title: '服务器连接失败', mask: true, icon: 'none', duration: 500 });
});
}
改造后的getConferenceList()方法内部返回一个promise对象,将获取大会信息列表之后的控制权交至消费者的手中,而本身只做一件事,获取大会消息列表。
这样一来,便可以控制调用setData()和stopPullDownRefresh()的时机,灵活度更高。
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.合理控制数据流
一个合格的前端开发工程师一定是善于控制数据流的,合理的控制数据流首先需要明确每个数据的生命周期,是伴随整个小程序的生命周期还是执行完某个函数就销毁了。确定了生命周期再选择存储数据的方式,是存储在localStorage、sessionStorage还是挂载到全局对象又或者仅仅需要路由传值,这些都需要根据数据的生命周期去考量。
喜欢这篇文章的朋友们,请点个赞再走哦~