为打通游戏人生擂台赛与线下商家的O2O衔接,同时响应时下日臻火热的微信小程序,项目团队决定也开发一款针对性的微信小程序,以此方便商家在咱们平台入驻并进行擂台赛事的建立和奖励的核销,进一步推广擂台赛的玩法模式和渠道来源。如下是咱们做为部门团队内第一批吃螃蟹者,在这款微信小程序开发过程当中踩过的一些坑以及总结,与你们一块儿分享,也欢迎指正和交流。php
做者:一时两无 | 腾讯互娱高级开发工程师 本文由微信平台开发发表在腾讯云+社区前端
目前【腾讯游戏人生】小程序已经发布上线,你们能够扫小程序码进行体验。接下来主要介绍在开发该款小程序过程当中的一些思考和积累。jquery
微信小程序是微信公众平台推出除服务号、订阅号、企业号外的第四种微信内应用类型,它是一种全新的链接用户与服务的方式,它能够在微信内被便捷地获取和传播,同时具备出色的仿原生app的交互使用体验和实用功能。web
咱们能够方便的在微信公众平台进行小程序的注册和提交资料,与微信公众号的注册流程较为一致。ajax
用户配置:小程序管理平台提供用户管理功能,支持添加1个管理员,根据账号类型和是否定证分别支持配置不一样数目的开发者和体验者账号权限,这些配置在小程序开发和内测阶段十分有用,便是一个官方的白名单配置功能。算法
开发配置:与微信公众号其余账号开发接入配置相似,须要分别设置开发者ID和密钥、服务器域名配置、开发消息接入地址等信息,可参考小程序开发文档逐一设置,对于有开发公众号经验的同窗来讲也比较快速入手,只是须要注意这里的域名接入都必需要是https的服务域名地址。json
小程序包含一个描述总体程序的 app 和多个描述各自页面的 page组成,能够看作是一系列页面的组合集成,由一个全局app对象调度运行。页面模型是小程序里的一个很重要的概念,从小程序配置文件app.json中也能够看到(以下所示),在app.json中注册的页面地址才能够被调用和打开展现。小程序的展现页面主要分为tabbar页和常规页两种,而只有tabbar页才会有底部tabbar显示,两类页面对应的跳转方式api也不一样:小程序
{ "pages": [ "page/xxx/x1", "page/yyy/y1" ], "window": { "navigationBarTitleText": "test" }, "tabBar": { "list": [{ "pagePath": "page/xxx/xxx", "iconPath": "image/xxx.png", "text": "tab1" }, { "pagePath": "page/yyy/yyy", "iconPath": "image/yyy.png", "text": "tab2" }] }, "networkTimeout": { "request": 10000, "uploadFile": 10000 }, "debug": true }
对于一个具体的页面模型,都有其内部独立的逻辑和数据做用域。主要包括四个组成文件,且必需要有相同的路径目录和文件名,例如:首页对应/page/index/目录下的index.js、index.wxml、index.wxss、index.json文件。微信小程序
页面的初始化、渲染、交互等逻辑均可以经过页面js进行事件监听和函数调用进行响应和处理,相似作web前端开发同样,只是须要特别注意该js开发与web前端js开发的部分不一样之处:api
小程序的运行和各页面的展现都有其特定的生命周期,并经过一系列的声明周期函数进行调度控制。例如app全局实例的onLaunch、onShow、onHide等监听函数来响应小程序初始化和显影时的控制逻辑。而对于page页面则拥有更为丰富的监听调控函数,实现页面生命周期中更多状况的控制处理。
函数定义 | 函数功能 |
---|---|
onLoad | 生命周期函数--监听页面加载 |
onReady | 生命周期函数--监听页面初次渲染完成 |
onShow | 生命周期函数--监听页面显示 |
onHide | 生命周期函数--监听页面隐藏 |
onUnload | 生命周期函数--监听页面卸载 |
onPullDownRefresh | 页面相关事件处理函数--监听用户下拉触底动做 |
onReachBottom | 页面相关事件处理函数--监听用户上拉触底动做 |
下图说明了小程序page页面实例的生命周期运做:
而针对小程序内部的多个页面之间的切换展现管理,则由小程序框架路由和页面栈控制托管,并经过路由标签或导航方式api函数进行页面切换。须要注意的是页面初始化第一次onLoad后若是只是onHide在后台不展现而并未onUnload销毁,下次再切回该页面展现时,不会再触发onLoad监听,而是触发onShow监听;onShow在页面的初始化或每次展现时都会触发,所以这里有个小技巧,部分须要实时更新展现到页面的数据可在onShow中进行获取处理。
小程序里的网络请求主要由wx.request(OBJECT)、wx.uploadFile(OBJECT)等api访问小程序配置的https域名url接口实现。前者相似于ajax请求,后者一般用来上传图片文件等。这里请求API有些坑须要注意:
小程序里的数据请求操做最好都须要进行登陆态安全校验,咱们在这里仿造以前作H5项目的微信受权校验方式,把调用微信登陆和受权后得到的openid等数据进行加密获取一个ticket票据,并设有过时时间,小程序的每一个数据请求则须要附加携带openid和该ticket参数在后台php里进行校验,成功则正常进行后续请求和返回数据,失败则告知小程序客户端从新登陆和受权后再请求数据。校验的核心算法也较为简单,就是判断在ticket有效期时间内是否知足以下等式:
而登陆和受权后初始的ticket生成也即用的该算法左式生成,并返回小程序本地缓存记录,下次请求可从缓存取出直接应用。
最后对小程序里的全部数据请求进行了处理,封装了GET/POST请求的header设置、登陆态参数的附加和过时处理、请求loading效果的显隐控制等逻辑,并设置在app全局对象的暴露方法httpRequest中,方便在各子页面调用处理。
因为咱们的小程序须要根据用户身份展示不一样状态的tabbar首页,所以须要把用户身份信息的请求前置,这里设计了一个loading过渡页面,且恰好在这个页面进行了微信登陆和受权,并获得登陆态参数初始化,而后请求了用户的身份后设置到app全局数据,并在tabbar首页进行对应判断和展现。
首页-默认 | 首页-待审核 | 首页-已经过 | 首页-已被拒 |
咱们的小程序里有须要商家注册和建立擂台的功能页面,须要填写的信息和层级较多,不足以一屏展现和填写,所以须要支持数据在跨页面间的传递和调用的通讯能力,且对数据进行完整、有效和安全的管理,并实时响应页面更新展现。基于小程序自己提供的api和特色,也查阅了一些资料,主要获得以下几种思路和方法:
类别 | 说明 | 优势 | 缺点 |
---|---|---|---|
navigate跳转+url参数 | 利用navigate标签或api跳转时附加须要的数据做为url参数一块儿跳转 | 不需额外代码或插件,代码简单 | 参数较多时url过长混乱、tabbar页不支持url传参数 |
缓存数据存储公用 | 利用app的globalData全局数据或本地缓存Storage的api实现数据缓存和公用处理 | 全局或本地缓存数据获取和改写,操做便捷有条理,容易理解 | 须要在相关各页面注意全局或缓存数据的改写和销毁,保持数据安全一致,避免被随意改写污染。 |
PubSub或watcher机制 | 利用事件发布订阅或监听机制,开发对应插件,各页面引入插件对所需数据进行订阅或监听处理。 | 基于插件化数据驱动实现,页面可按需加入订阅或监听数据的处理,单一数据源,便于调试 | 需引入额外插件,且需解决页面屡次show、hide等致使的重复订阅或监听绑定,有程序崩溃风险 |
页面路由栈 | 利用小程序自带getCurrentPages的api获取页面路由栈,并根据页面序号获取和操做所需页面对象 | 代码逻辑清晰,能更为便捷获取和处理所需页面对象的数据,数据只有原页面对象单独一份,且由其内部托管销毁,无数据污染风险。在目标页面对此数据的修改可经过setData实时响应更新到原页面展现 | 须要稍微注意页面栈深度变化和所需页面对象的获取 |
咱们考虑到表单数据较多,且产品需求表单须要本地草稿的功能,下次再打开可显示上次填写数据,无需从新再次填写,所以最终结合了缓存和页面路由栈的功能进行实现。在表单主页面A利用localStorage缓存托管表单全体数据formData,并在子页面B用页面栈getCurrentPages获取和操做主页面A的表单某块子数据formData.subData,子页面B的修改操做经过A.setData实时传递和通知主页面A的刷新展现,主页面A在onUnload中响应对localstoreage的修改保存,便于下次加载读取。
小程序代码中涉及的较多数据、参数、接口、文案等自定义信息,可作成统一本地化配置,放入app实例的全局数据中公用,便于各子页面获取处理,同时结合小程序loading初始化时进行远程请求更新配置。这样的好处是,能够兼容配置信息更新与否状况下的配置统一管理。当须要配置更新时,能从远程拉取替换,而不须要修改小程序的代码文件,从新再走代码发布及等待审核的流程。
小程序中注册商家资料和建立擂台时都涉及到了图片的上传处理,用到了小程序官方的传图样式组件和API,同时须要调用统一的后台上传图片生成URL的接口。所以这里有必要能够进行组件模块化封装的代码优化,便于在多个page页面内引入调用。
<template name="picloader"> <view class="weui-cells weui-cells_after-title"> <view class="weui-cell"> <view class="weui-cell__bd"> <view class="weui-uploader"> <view class="weui-uploader__hd"> <view class="weui-uploader__title">{{title}}</view> </view> <view class="weui-uploader__bd"> <view class="weui-uploader__files"> <block wx:if="{{picture}}"> <view class="weui-uploader__file" bindtap="previewImage" data-obj="{{name}}"> <image class="weui-uploader__img" src="{{picture}}" mode="aspectFill" /> </view> </block> <input id="{{name}}" name="{{name}}" hidden="{{true}}" value="{{picture}}"/> </view> <view class="weui-uploader__input-box"> <view class="weui-uploader__input" bindtap="chooseImage" data-obj="{{name}}"></view> </view> </view> </view> </view> <view class="weui-cell__ft"><icon type="{{validate}}"/></view> </view> </view> </template>
const app = getApp(); function init(pageDelegate) { //1.初始化图片上传种子HASH值 app.httpRequest({ url:app.Utils.getRequestUrl("getUploadHash"), success: function( res ) { if(res.r== "0"){ pageDelegate.setData({ _hash:res._hash }); } }, },false); //2.绑定选择图片事件 pageDelegate.chooseImage = function (e) { var that = this; var uploadUrl = app.Config.uploadBase; var obj = e.currentTarget.dataset.obj;//修改对象名 if (e.currentTarget.dataset.ratio) {//尺寸比例限制 uploadUrl += "?size_ratio=" + e.currentTarget.dataset.ratio; } wx.chooseImage({ sizeType: ['original', 'compressed'], // 能够指定是原图仍是压缩图,默认两者都有 sourceType: ['album', 'camera'], // 能够指定来源是相册仍是相机,默认两者都有 count:1, success: function (res0) { // 返回选定照片的本地文件路径列表,tempFilePath能够做为img标签的src属性显示图片 app.uploadRequest({ url:uploadUrl, filePath: res0.tempFilePaths[0], data:{ _hash:that.data._hash }, success:function(res){ var picurl = res.url || ""; var tmpData = {}; tmpData["formData." + obj] = picurl; that.setData(tmpData); app.Utils.checkValid(obj, picurl,that); if (res.r != "0" && res.msg){ wx.showModal({ title: '图片上传失败', content: res.msg, showCancel: false, success: function (res) { } }); } } }) } }) } //3.绑定预览图片事件 pageDelegate.previewImage = function (e) { var obj = e.currentTarget.dataset.obj;//修改对象名 var pic = this.data.formData[obj] || ""; if(pic == ""){ return false; } wx.previewImage({ current: e.currentTarget.id, urls: [pic] // 须要预览的图片http连接 }); } } //模块化 module.exports = { init: init }
<import src="/page/common/picloader.wxml"/> <template is="picloader" data="{{title: '奖励图片上传',picture:formData.award_pic,validate:validate.award_pic,name:'award_pic'}}"/>
const app = getApp(); var picloader = require('/utils/picloader.js'); Page({ data:{ ... }, onLoad:function(options){ // 页面初始化 options为页面跳转所带来的参数 //注册图片上传组件 picloader.init(this); }, ... })
小程序tabbar首页的需求是根据不一样的用户身份展示不一样状态的首页,有未入驻、待审核、审核经过、审核被拒四种状态,而都须要对应到同一个tabbar首页url。所以这里须要把四种状态的页面片断部分分别作成子模版wxml的形式,经过小程序的条件渲染(wx:if)机制根据用户身份状况按条件调用对应子模版进行展现。
同时小程序较多页面都有共同的头部(banner图)和尾部(联系客服)等片断展现,所以这里也考虑把其作成对应的公用head和foot子模版wxml,便于多页面include引用。
<view class="page"> <include src="/page/common/head.wxml"/> <view class="weui-msg"> <include wx:if="{{status == 1}}" src="subpage/wait.wxml"/> <include wx:elif="{{status == 2}}" src="subpage/success.wxml"/> <include wx:elif="{{status == 3}}" src="subpage/fail.wxml"/> <include wx:else src="subpage/default.wxml"/> </view> </view> <include src="/page/common/foot.wxml"/>
【腾讯游戏人生】微信小程序开发已经结束,亟待补充产品条款以及发布审核上线。在整个摸索和开发过程当中,碰到了许多与web开发不一样的别扭之处,也填过很多坑,包括参与小程序实现的设计、重构和前端开发都是一个新的尝试与体验。也对此有一些思考和总结,具体以下概括。目前感受小程序比较适用于一些旨在更快速和有效推广本身轻量功能的小应用模式,不适合较大较重逻辑和功能的开发应用。但相信随着微信官方对小程序支持力度的不断增长,小程序的功能和推广也将获得进一步扩大,接入和开发成本的同步下降,也会受到愈来愈多的开发者欢迎和喜好。
优点 | 劣势 |
---|---|
接入门槛低,参照公众号接入方便,当下热潮 | 官方设定的配置和开发模式严格,需限定范围内进行搭建 |
使用体验较H5好,流畅度高,体积小,传播效益快 | 功能体验限于限有组件交互,定制扩展化较弱,相比原生app体验及高级功能仍是较少 |
学习成本低,代码组织清晰,开发灵活度高,开发文档明确 | 不支持web开发中部分经常使用的功能特性,有时开发有些不便和局限性。 |
微信官方支持度高,内嵌灵活,小程序闭环控制,数据隔离安全可靠 | 暂不支持跳转H5网页,以及外部APP等交互能力。 |
此文已由做者受权腾讯云+社区发布,原文连接:https://cloud.tencent.com/developer/article/1145916?fromSource=waitui
欢迎你们前往腾讯云+社区或关注云加社区微信公众号(QcloudCommunity),第一时间获取更多海量技术实践干货哦~