mpvue开发大型体育项目及总结记

Html5 · wowser · 2019-01-12 · 1 次阅读

文章来源: https://juejin.im/post/5c2d8073e51d455405559941, 本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。

最近接到上头的指示,要做一个体育类的小程序,并且要在元旦之前上线一版,看了下时间,距离元旦只有一个多月,而且除去测试的时间和双休,最多只有三个星期,时间相当的紧迫,而且需求文档都更新到1.3了,这也就意味着安卓和 ios 的版本迭代已经到了1.3了,而我们小程序要在三个星期内开发完1.0-1.3版本的功能,所以我们的时间相当的紧迫,看了下需求文档和原型图,我整个人大吃一惊,有100多个页面,我顿时懵逼了,产品经理更我说;你们只需做三个模块,最后一个模块不用做,我看了一下最后一个模块,有10几个页面,除去10几个页面还有90多个呀,页面还好,最难的是需求文档的业务逻辑呀,而且这个项目的难度比中小型电商项目类的要难的多,交互相当的频繁和复杂,怎么办呢,一句话,凉拌,毕竟这是boss要求做的项目,只好硬着头皮往下做,不得不从。

技术选型

因小程序页面多,切交互频繁,如果用原生开发的话,时间来不急,而且页面的交互很多,这样原生小程序就显得那么的吃力了,最终我将技术选型放在了,wepy和mpvue这两个框架上,wepy框架是微信官方维护的,兼容性和扩展性很好,mpvue是美团旗下的,我最终选择了mpvue,原因是mpvue的语法跟Vue的语法是一样的,而且我们的前端同学都会Vue,所以选择mpvue是最好的选择,于是看了一下mpvue的文档和注意点,最终搭建了小程序的项目结构,将任务安排了下去,于是开启了加班的苦日子....

  • 技术点1-小程序,安卓,iOS三端数据信息同步,免登陆

因项目中的登录方式含有微信登录,所以三端协商,如果用户是微信登录的话,三端统一取, 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的,我们找到原因之后,马上换了另外一种方法,那就是解码的方式,也是我们最终的方法。

<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解码地址下载 , 注意:解码这一步最好放在服务端解码,不要放在客户端解码,这样会造成信息泄露.....

  • 技术点2-在小程序中使用canvas

mpvue开发大型体育项目及总结记
mpvue开发大型体育项目及总结记
我们可以看到上面两个项目中的案例图片,他们是用canvas画的,第一个是采用微信官方的api, 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会数据不同步,具体的做法可以加一个开关,如下..

<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) {}
   }
   //注意:以上代码只是模拟,仅供查考。
复制代码
  • 技术点3-图片上传转化base64

图片上传微信小程序给我们提供了api, wx.chooseImage ,上传简单,关键是如何转化base64位呢,我们的舒同学用了如下的写法,看着确实没什么问题,用临时路径作为一个请求的url,把数据返回格式设置成arraybuffer,这个也确实是个办法,在微信开发 工具 里面也是ok的,但是天有不测风云呀,在真机上请求报错了,那么这种方法pass掉。

wx.chooseImage({
      success:res=>{
            wx.request({
                  url:url,
                  responseType: 'arraybuffer', //最关键的参数,设置返回的数据格式为arraybuffer
                  success:res=>{
                        let base64 = wx.arrayBufferToBase64(res.data); 
                      }
           })
})
复制代码

针对上面的问题,仔细的看了下微信官方文档,最终找到了一个代码少,简单的方法, wx.getFileSystemManager() 这个api可以解决我们上面的问题,微信官方文档,代码如下

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度的问题 小程序可以借鉴 这个同学的方法来解决 ,如果是 html 5的话可以通过 exif.js 这个库来解决问题。

  • 技术点4-对picker的封装

小程序中有个picker组件,他支持5中类型,虽然有5中类型但是每个项目的不同,所以对picker的用途就不同,因此我们将对picker进行封装,来达到满足我们项目的需求,我们封装省市,时间日期等组件,我这里就只介绍 省市 组件的封装,其他的组件封装原理同省市组件原理一样的,我这里就不多说了,代码如下。

<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;
            }
          },
      }
  }
复制代码
mpvue开发大型体育项目及总结记
  • 技术点5-在小程序使用高德定位

因项目中要用到定位功能,而小程序中的的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','定位失败');
                        }
                    }
                })
        }
    });
复制代码
  • 技术点6-对微信小程序节点的运用

由于项目用到了城市索引选择功能,所以就采用 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) {}
            },
    }
复制代码
mpvue开发大型体育项目及总结记
当然实现上面这个功能也可以用其他的方法,如scroll-view,我这里就不多说了。
  • 技术点7-返回上一层页面,刷新页面数据

我们可以通过微信中的 wx.navigateBack() 这个api就可以返回上一层页面,但是怎样返回上一层页面并且刷新呢,其实可以通过onShow这个生命周期函数来刷新页面,如果那个页面含有参数的话,最好代码这么写

onShow(){
//之所以用try,是因为mpvue官方说,如果要获取地址参数的话,最好在mounted周期里面获取,我们用try可以避免代码终止和报错
     try {   
         let id=this.$root.$mp.query.Id;
         this.init(id);
     }catch (error) {}
}
复制代码

结语

由于时间的原因,我暂时先介绍这几个在小程序中常见的问题和功能,后面我会陆续介绍,如下技术栈

  • vue的三种ssr方法,以及在项目中的使用
  • react+redux在项目中的使用
  • 打造自已的webpack,gulp开发环境
  • koa框架的介绍和使用

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

码农书籍
你不知道的JavaScript(上卷)

你不知道的JavaScript(上卷)

[美] Kyle Simpson / 赵望野、梁杰 / 人民邮电出版社 / 2015-4 / 49.00元

JavaScript语言有很多复杂的概念,但却用简单的方式体现出来(比如回调函数),因此,JavaScript开发者无需理解语言内部的原理,就能编写出功能全面的程序;就像收音机一样,你无需理解里面的管子和线圈都是做什么用的,只要会操作收音机上的按键,就可以收听你喜欢的节目。然而,JavaScript的这些复杂精妙的概念才是语言的精髓,即使是经验丰富的JavaScript开发者,如果没有认真学习也无法真正理解语言本身的特性。正是因为绝大多数人不求甚解,一遇到出乎意料的行为就认为是语言本身有缺陷,进而把相关的特性加入黑名单,久而久之就排除了这门语言的多样性,人为地使它变得不完整、不安全。“你不知道的JavaScript”系列就是要让不求甚解的JavaScript开发者迎难而上,深入语言内部,弄清楚JavaScript每一个零部件的用途。本书介绍了该系列...

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具