最近接到上头的指示,要作一个体育类的小程序,而且要在元旦以前上线一版,看了下时间,距离元旦只有一个多月,并且除去测试的时间和双休,最多只有三个星期,时间至关的紧迫,并且需求文档都更新到1.3了,这也就意味着安卓和ios的版本迭代已经到了1.3了,而咱们小程序要在三个星期内开发完1.0-1.3版本的功能,因此咱们的时间至关的紧迫,看了下需求文档和原型图,我整我的大吃一惊,有100多个页面,我顿时懵逼了,产品经理更我说;大家只需作三个模块,最后一个模块不用作,我看了一下最后一个模块,有10几个页面,除去10几个页面还有90多个呀,页面还好,最难的是需求文档的业务逻辑呀,并且这个项目的难度比中小型电商项目类的要难的多,交互至关的频繁和复杂,怎么办呢,一句话,凉拌,毕竟这是boss要求作的项目,只好硬着头皮往下作,不得不从。javascript
因小程序页面多,切交互频繁,若是用原生开发的话,时间来不急,并且页面的交互不少,这样原生小程序就显得那么的吃力了,最终我将技术选型放在了,wepy和mpvue这两个框架上,wepy框架是微信官方维护的,兼容性和扩展性很好,mpvue是美团旗下的,我最终选择了mpvue,缘由是mpvue的语法跟Vue的语法是同样的,并且咱们的前端同窗都会Vue,因此选择mpvue是最好的选择,因而看了一下mpvue的文档和注意点,最终搭建了小程序的项目结构,将任务安排了下去,因而开启了加班的苦日子....html
因项目中的登陆方式含有微信登陆,因此三端协商,若是用户是微信登陆的话,三端统一取,unionid
这个字段,这时确定有同窗要问;为何不取openid
,若是作太小程序的人必定知道openid是惟一的标识,微信小程序有一个,那么在安卓,iOS他们也都有自已的openid标识,因此这样是不能达到三端数据信息同步,免登录的效果的,微信官方介绍了6种获取unionid
的方法,咱们项目最终采用了解密获取的方法,官方文档。前端
import {AchieveOpenid} from '@/http/api.js';
let that=this;
wx.login({
async success(resCode){
const cache=await AchieveOpenid({ //openid,内部服务器=》腾讯获取到了openid
code:resCode.code
});
that.openid=cache.result.unionid;
wx.setStorageSync('openId',that.openid);
}
})
复制代码
注意:这个方法是官方中的wx.login+code2Session方法,也确实能够获取unionid,可是天有不测风云呀,测试组的人员测出了unionid不存在的状况,并且还有几个帐号也出现了这种问题,那么咱们就开始找缘由,最终咱们发现,若是用户没有关注过任何的公众号,微信是不会给他返回unionid的,咱们找到缘由以后,立刻换了另一种方法,那就是解码的方式,也是咱们最终的方法。
vue
<button class='openpage-authorize' open-type="getUserInfo" lang="zh_CN" @getuserinfo="onGotUserInfo">
</button>
复制代码
//注意我这里只列举解码的代码,有些代码省略了,请熟知。
import WXBizDataCrypt from "@/utils/cryptojs/RdWXBizDataCrypt.js" //引用解码
methods:{
deCode(encryptedData,iv,sessionKey){
let wxObj=null,data=null;
wxObj= new WXBizDataCrypt('wx3ea59bf3ff3a9bb8', sessionKey);
data= wxObj.decryptData(encryptedData,iv);
this.openid=data.unionId;
wx.setStorageSync('openId',data.unionId);
},
onGotUserInfo(e) { //经过按钮触发getuserinfo
if(e.mp.detail.userInfo){
this.deCode(e.mp.detail.encryptedData,e.mp.detail.iv,this.sessionKey);
}else{
toast('请再次受权');
}
},
}
复制代码
最终咱们能够经过上面的代码获取unionId
,解码地址下载,注意:解码这一步最好放在服务端解码,不要放在客户端解码,这样会形成信息泄露.....
html5
wx.createCanvasContext
不懂得同窗能够自已去看
微信官方文档,代码以下
const ctx = wx.createCanvasContext('myCanvas');
ctx.setLineCap('round')
var gradient1=ctx.createLinearGradient(0,0,170,0);
gradient1.addColorStop("0","#FFF956");
gradient1.addColorStop("1.0","#FF6C00");
var gradient2=ctx.createLinearGradient(0,0,170,0);
gradient2.addColorStop("0","#8156FE");
gradient2.addColorStop("1.0","#3AFFF1");
ctx.setLineWidth(4);
ctx.beginPath();
ctx.arc(50, 50, 30,0.75*Math.PI,0.25*Math.PI,false);
ctx.setStrokeStyle('#4e4f59');
ctx.stroke()
ctx.beginPath();
ctx.arc(50, 50, 38,0.75*Math.PI,0.25*Math.PI,false);
ctx.setStrokeStyle('#4e4f59');
ctx.stroke();
//胜
ctx.beginPath();
ctx.arc(50, 50, 30,0.75*Math.PI,(((winarc*1.5+0.75)%2)==0?2:((winarc*1.5+0.75)%2))*Math.PI,false);
ctx.setStrokeStyle(gradient1)
ctx.stroke()
//负
ctx.beginPath();
ctx.arc(50, 50, 38,0.75*Math.PI,(((failarc*1.5+0.75)%2)==0?2:((failarc*1.5+0.75)%2))*Math.PI,false)
ctx.setStrokeStyle(gradient2);
ctx.stroke()
ctx.setTextAlign('center');
ctx.setFontSize(16);
ctx.setFillStyle('#fff');
ctx.setTextBaseline('middle');
ctx.fillText('战绩', 50, 50);
ctx.draw()
复制代码
注意点:若是canva的数据是异步的话,必定要在数据加载完成以后,在让它渲染到视图层中去,若是不这样作的话,canvas会数据不一样步,具体的作法能够加一个开关,以下..
java
<canvas canvas-id="myCanvas" class="index-header-data-circle-canvas" v-if='on'></canvas>
复制代码
data(){
return {
on:false
}
}
async xx(){
try{
const data=await xxx();
this.on=data.code==='000'?true:false;
}catch (error) {}
}
//注意:以上代码只是模拟,仅供查考。
复制代码
图片上传微信小程序给咱们提供了api,wx.chooseImage
,上传简单,关键是如何转化base64位呢,咱们的舒同窗用了以下的写法,看着确实没什么问题,用临时路径做为一个请求的url,把数据返回格式设置成arraybuffer,这个也确实是个办法,在微信开发工具里面也是ok的,可是天有不测风云呀,在真机上请求报错了,那么这种方法pass掉。react
wx.chooseImage({
success:res=>{
wx.request({
url:url,
responseType: 'arraybuffer', //最关键的参数,设置返回的数据格式为arraybuffer
success:res=>{
let base64 = wx.arrayBufferToBase64(res.data);
}
})
})
复制代码
针对上面的问题,仔细的看了下微信官方文档,最终找到了一个代码少,简单的方法,wx.getFileSystemManager()
这个api能够解决咱们上面的问题,微信官方文档,代码以下webpack
wx.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success:(res)=>{
wx.getFileSystemManager().readFile({
filePath:res.tempFilePaths[0], //选择图片返回的相对路径
encoding: 'base64',
success: res => {
console.log(res.data)
}
})
}
})
复制代码
其实除了,wx.getFileSystemManager()
能够解决咱们的问题外,还有一种方法,那就是更html5同样的处理方法,经过canvas来画,而后在用canvas的api来转base64,注意:若是经过canvas来转base64的话,有个bug,那就是在iOS手机上图片会出现旋转90度的问题
小程序能够借鉴这个同窗的方法来解决,若是是html5的话能够经过exif.js这个库来解决问题。ios
小程序中有个picker组件,他支持5中类型,虽然有5中类型可是每一个项目的不一样,因此对picker的用途就不一样,所以咱们将对picker进行封装,来达到知足咱们项目的需求,咱们封装省市,时间日期等组件,我这里就只介绍省市
组件的封装,其余的组件封装原理同省市组件原理同样的,我这里就很少说了,代码以下。git
<picker class="pickes" mode="multiSelector" @change="PickerChange" @columnchange="PickerColumnChange" :range="allList" range-key='provinceName' :value='multiIndex' v-if='show'>
<div class="slot"></div>
</picker>
复制代码
/** * @describe 省市选择器 * @rerurn 省,市,省id,市id */
import {allCity} from "@/http/api.js";
export default {
data() {
return {
list:[],
multiIndex: [0, 0], //显示化动的列数
allList:[], //存储二维数据
singleList:[], //存储一维数组
show:false, //防止数据没有加载出来
cityInfo:{}, //存储省,市,省id,市id
}
},
mounted(){
this.init();
},
methods:{
async init(){
try {
let child=[],data=null;
data=await allCity(); //获取后台返回的城市
this.singleList=data.result;
child.push(data.result[0]);
this.allList.push(data.result,child);
this.show=true;
} catch (error) {}
},
PickerChange(e) {
this.cityInfo.provinceId=this.singleList[e.mp.detail.value[0]].provinceId; //省id
this.cityInfo.provinceName=this.singleList[e.mp.detail.value[0]].provinceName; //省名
this.cityInfo.cityId=this.singleList[e.mp.detail.value[0]].cityList[e.mp.detail.value[1]].cityId; //城市id
this.cityInfo.cityName=this.singleList[e.mp.detail.value[0]].cityList[e.mp.detail.value[1]].cityName; //城市名
this.$emit('cityInfo',this.cityInfo); //将值传给父组件
},
PickerColumnChange(e) {
switch (e.mp.detail.column) {
case 0:
this.list = [];
this.singleList.forEach(item => {
if (item.provinceId ==this.singleList[e.mp.detail.value].provinceId) {
item.cityList.forEach(item=>{
//注意这一步最为重要,给数组添加一个和父对象同样的键值名,这样picker组件能够找的到
item.provinceName=item.cityName;
})
}
});
this.allList[1]=this.list;
this.multiIndex[0]=e.mp.detail.value;
this.multiIndex[1]=0; //注意这个表示的时选择中省切换的时候,要将省的第一个城市放在第一位
break;
}
},
}
}
复制代码
因项目中要用到定位功能,而小程序中的的api并不适用项目,因此就选择了高德定位,高德小程序版文档,代码以下
//注意我这里只列举定位的代码,有些代码省略了,请熟知。
let _this = this,myAmapFun=null;
myAmapFun = new amapFile.AMapWX({
key: "xxxxx" //高德的密钥
});
myAmapFun.getRegeo({
success(data){
_this.$store.dispatch('cityLocal',data[0].regeocodeData.addressComponent.city);
},
fail(err) {
wx.showModal({
title: '提示',
content: '定位失败,请手动定位',
success (res) {
if (res.confirm) {
path({url:'/pages/city/main'});
}else if (res.cancel) {
_this.$store.dispatch('cityLocal','定位失败');
}
}
})
}
});
复制代码
因为项目用到了城市索引选择功能,因此就采用 wx.createSelectorQuery()
这个api来实现这个功能,代码以下
<ul class="slide">
<li v-for="(item,index) in cityJson.leter" :key="index" @tap='touStart(item.letid,item.lettext)'>
{{item.lettext}}
</li>
</ul>
复制代码
//注意我这里只列举城市索引选择的代码,有些代码省略了,请熟知。
onPageScroll(e){
this.scollTop=e.scrollTop //同步
},
methods:{
touStart(flag,text){
try {
wx.createSelectorQuery().select(flag).fields({ //运用微信节点api
dataset: true,
size: true,
rect: true,
computedStyle: ['margin', 'backgroundColor']
}, (res)=> {
wx.pageScrollTo({
scrollTop: this.scollTop+res.top,
duration: 0
});
this.on=true;
this.modalText=text;
setTimeout(()=>{
this.on=false;
},2000)
}).exec()
}catch (error) {}
},
}
复制代码
固然实现上面这个功能也能够用其余的方法,如scroll-view,我这里就很少说了。
咱们能够经过微信中的wx.navigateBack()
这个api就能够返回上一层页面,可是怎样返回上一层页面而且刷新呢,其实能够经过onShow这个生命周期函数来刷新页面,若是那个页面含有参数的话,最好代码这么写
onShow(){
//之因此用try,是由于mpvue官方说,若是要获取地址参数的话,最好在mounted周期里面获取,咱们用try能够避免代码终止和报错
try {
let id=this.$root.$mp.query.Id;
this.init(id);
}catch (error) {}
}
复制代码
因为时间的缘由,我暂时先介绍这几个在小程序中常见的问题和功能,后面我会陆续介绍,以下技术栈