【腾讯游戏人生】微信小程序开发总结

| 导语 为打通游戏人生擂台赛与线下商家的O2O衔接,同时响应时下日臻火热的微信小程序,项目团队决定也开发一款针对性的微信小程序,以此方便商家在我们平台入驻并进行擂台赛事的创建和奖励的核销,进一步推广擂台赛的玩法模式和渠道来源。以下是我们作为部门团队内第一批吃螃蟹者,在这款微信小程序开发过程中踩过的一些坑以及总结,与大家一起分享,也欢迎指正和交流。

目前【腾讯游戏人生】小程序已经发布上线,大家可以扫小程序码进行体验。接下来主要介绍在开发该款小程序过程中的一些思考和积累。

一、基础普及

1.1简介

微信小程序是微信公众平台推出除服务号、订阅号、企业号外的第四种微信内应用类型,它是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的仿原生app的交互使用体验和实用功能。

我们可以方便的在微信公众平台进行小程序的注册和提交资料,与微信公众号的注册流程较为一致。对于公司内部的小程序也可以在公司内部平台进行快速注册和审核,地址为http://mmbiz.oa.com/inneraccount?action=index,在此不具体赘述。

1.2配置

用户配置:小程序管理平台提供用户管理功能,支持添加1个管理员,根据帐号类型和是否认证分别支持配置不同数目的开发者体验者帐号权限,这些配置在小程序开发和内测阶段十分有用,即是一个官方的白名单配置功能。

开发配置:与微信公众号其他帐号开发接入配置类似,需要分别设置开发者ID和密钥服务器域名配置开发消息接入地址等信息,可参考小程序开发文档逐一设置,对于有开发公众号经验的同学来说也比较快速入手,只是需要注意这里的域名接入都必须要是https的服务域名地址

二、开发注意

2.1页面模型

小程序包含一个描述整体程序的 app 和多个描述各自页面的 page组成,可以看做是一系列页面的组合集成,由一个全局app对象调度运行。页面模型是小程序里的一个很重要的概念,从小程序配置文件app.json中也可以看到(如下所示),在app.json中注册的页面地址才可以被调用和打开展示。小程序的展示页面主要分为tabbar页常规页两种,而只有tabbar页才会有底部tabbar显示,两类页面对应的跳转方式api也不同:

  • 对于tabbar页地址(例 page/xxx/xxx),调用wx.switchTab(OBJECT)进行跳转;
  • 对于常规页地址(例 page/xxx/x1),调用wx.navigateTo(OBJECT)wx.redirectTo(OBJECT)进行跳转
{
  "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开发的部分不同之处:

  • 页面逻辑运行在Jscore中,非webview,无window、body、document等dom对象结构;
  • 无页面cookie,无法设置网络请求header的refer
  • 不支持类似jquery、zepto等对象拾取插件和操作方式,视图更新通过数据绑定方式实现;
  • 页面展示结构主要由官方原生组件拼装展示,并通过对应支持的有限的事件函数进行响应控制,扩展化较低;
  • 页面数据的改变通过调用Page.setData函数回显页面组件展示,页面组件的动作事件值;e.detail.value可以传递给事件响应函数重设页面数据,以此达到页面数据和组件的联动绑定

2.2生命周期

小程序的运行和各页面的展示都有其特定的生命周期,并通过一系列的声明周期函数进行调度控制。例如app全局实例的onLaunch、onShow、onHide等监听函数来响应小程序初始化和显影时的控制逻辑。而对于page页面则拥有更为丰富的监听调控函数,实现页面生命周期中更多情况的控制处理。

函数定义

函数功能

onLoad

生命周期函数--监听页面加载

onReady

生命周期函数--监听页面初次渲染完成

onShow

生命周期函数--监听页面显示

onHide

生命周期函数--监听页面隐藏

onUnload

生命周期函数--监听页面卸载

onPullDownRefresh

页面相关事件处理函数--监听用户下拉触底动作

onReachBottom

页面相关事件处理函数--监听用户上拉触底动作

下图说明了小程序page页面实例的生命周期运作:

而针对小程序内部的多个页面之间的切换展示管理,则由小程序框架路由和页面栈控制托管,并通过路由标签或导航方式api函数进行页面切换。需要注意的是页面初始化第一次onLoad后如果只是onHide在后台不展示而并未onUnload销毁,下次再切回该页面展示时,不会再触发onLoad监听,而是触发onShow监听;onShow在页面的初始化或每次展示时都会触发,因此这里有个小技巧,部分需要实时更新展示到页面的数据可在onShow中进行获取处理。

三、数据处理

3.1数据请求

小程序里的网络请求主要由wx.request(OBJECT)、wx.uploadFile(OBJECT)等api访问小程序配置的https域名url接口实现。前者类似于ajax请求,后者通常用来上传图片文件等。这里请求API有些坑需要注意:

  • 请求不支持设置header的refer;
  • 请求url不允许带自定义端口,只能是默认80端口;
  • 请求content-type默认为'application/json',如需用POST请求则需改为'application/x-www-form-urlencoded''multipart/form-data',否则后台请求里得不到post数据;
  • 后台接收请求php里最好用json_decode(file_get_contents("php://input"))方式获取完整的post数据,否则如果传递较为复杂的多层post数据结构体,直接用$_POST等可能导致获取数据格式异常或失败

小程序里的数据请求操作最好都需要进行登录态安全校验,我们在这里仿造之前做H5项目的微信授权校验方式,把调用微信登录和授权后获得的openid等数据进行加密获取一个ticket票据,并设有过期时间,小程序的每个数据请求则需要附加携带openid和该ticket参数在后台php里进行校验,成功则正常进行后续请求和返回数据,失败则告知小程序客户端重新登录和授权后再请求数据。校验的核心算法也较为简单,就是判断在ticket有效期时间内是否满足如下等式:

sha1(APPID@openid@TOKEN)  ==  ticket

而登录和授权后初始的ticket生成也即用的该算法左式生成,并返回小程序本地缓存记录,下次请求可从缓存取出直接应用。

最后对小程序里的所有数据请求进行了处理,封装了GET/POST请求的header设置、登录态参数的附加和过期处理、请求loading效果的显隐控制等逻辑,并设置在app全局对象的暴露方法httpRequest中,方便在各子页面调用处理。

由于我们的小程序需要根据用户身份展现不同状态的tabbar首页,因此需要把用户身份信息的请求前置,这里设计了一个loading过渡页面,且刚好在这个页面进行了微信登录和授权,并得到登录态参数初始化,然后请求了用户的身份后设置到app全局数据,并在tabbar首页进行对应判断和展示。

首页-默认

首页-待审核

首页-已通过

首页-已被拒

3.2页面通信

我们的小程序里有需要商家注册和创建擂台的功能页面,需要填写的信息和层级较多,不足以一屏展示和填写,因此需要支持数据在跨页面间的传递和调用的通信能力,且对数据进行完整、有效和安全的管理,并实时响应页面更新展示。基于小程序本身提供的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的修改保存,便于下次加载读取。

四、代码维护

4.1公用配置

小程序代码中涉及的较多数据、参数、接口、文案等自定义信息,可做成统一本地化配置,放入app实例的全局数据中公用,便于各子页面获取处理,同时结合小程序loading初始化时进行远程请求更新配置。这样的好处是,可以兼容配置信息更新与否情况下的配置统一管理。当需要配置更新时,能从远程拉取替换,而不需要修改小程序的代码文件,重新再走代码发布及等待审核的流程。

4.2传图组件

小程序中注册商家资料和创建擂台时都涉及到了图片的上传处理,用到了小程序官方的传图样式组件和API,同时需要调用统一的后台上传图片生成URL的接口。因此这里有必要可以进行组件模块化封装的代码优化,便于在多个page页面内引入调用。

  • picloader.wxml
<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>
  • picloader.js
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
}
  •  使用示例wxml中:
<import src="/page/common/picloader.wxml"/>
<template is="picloader" data="{{title: '奖励图片上传',picture:formData.award_pic,validate:validate.award_pic,name:'award_pic'}}"/>
  • 使用示例js中:
const app = getApp();
var picloader = require('/utils/picloader.js');

Page({
  data:{
      ...
  },
  onLoad:function(options){
      // 页面初始化 options为页面跳转所带来的参数

      //注册图片上传组件
      picloader.init(this);
  },
  ...
})

4.3分片模版

小程序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等交互能力。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

微信平台开发

1 篇文章3 人订阅

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏文大师的新世界

VUX 单页微信开发的那些事

VUX是微信推出的WeUI的非官方VUE的非官方实现,主要针对移动端微信前端开发,属于个人项目,目前在GitHub已有8k star,使用webpack 2以及...

1325
来自专栏VMCloud

【解析向】腾讯云的Windows Server日志配置收集工具是个什么鬼?(2)

继上一篇,上一篇重点介绍了腾讯云Windows Server日志收集工具的“场景”功能,那么场景功能究竟是以什么标准来分级的呢?

3687
来自专栏java一日一条

Spring 测试:其实很简单

在过去的职业生涯里,我经常发现有些人不写测试代码,而他们声称不写的理由是无法轻易地写出覆盖多个不同模块的测试用例。好吧,我相信他们中的大部分要么是缺乏一些比较易...

912
来自专栏前端黑板报

你真的了解 Web 缓存体系吗?

很高兴认识大家,之前做过很多分享,今天这次终于讲到正题了。因为之前一直讲自动化运维,其实做这么多年运维,自动化运维没干多少年。这几年很多公司各方面机器数量多了,...

851
来自专栏blackheart的专栏

[解读REST] 4.基于网络应用的架构风格

衔接上文[解读REST] 3.基于网络应用的架构,上文介绍了一组自洽的术语来描述和解释软件架构;如何利用架构属性评估一个架构风格;以及对于基于网络的应用架构来说...

1785
来自专栏杨建荣的学习笔记

Datapump数据迁移前的准备工作(r9笔记第31天)

其实对于Datapump迁移而言,如果参与过XTTS,OGG,Veritas SF,外部表增量等迁移方式的话,会发现Datapump还是很简单清晰的,一个优点...

2573
来自专栏DevOps时代的专栏

维护了这么久的服务器,你真的认识 Web 缓存体系?

前言 很高兴认识大家,之前做过很多分享,今天这次终于讲到正题了。因为之前一直讲自动化运维,其实做这么多年运维,自动化运维没干多少年。这几年很多公司各方面机器数量...

2208
来自专栏前端之路

–Vue-cli引入lib-flexible&&ElementUI

1034
来自专栏Java帮帮-微信公众号-技术文章全总结

MQ消息中间件(工作+面试)

MQ消息中间件(工作+面试) ? AMQP协议介绍 AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协...

5907
来自专栏社区的朋友们

深入浅出 Nodejs ( 一 ) :Nodejs 的简介

我认为 Node 是一门独具风格的技术,它的特点很有意思,本章我们主要讲 Node 的特点,Node 应用场景以及 Node 的使用者。

2970

扫码关注云+社区