用户
 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,登录网站

小程序社区 首页 教程 官方教程 查看内容

A计划小程序的血与泪

Rolan 2018-11-8 00:21

A计划只是一个代号,不代表任何小程序微信小程序,简称小程序,英文名Mini Program,是一种不需要下载安装即可使用的应用,它实现了应用“触手可及”的梦想,用户扫一扫或搜一下即可打开应用。最近两周由于公司业务 ...

A计划只是一个代号,不代表任何小程序

微信小程序,简称小程序,英文名Mini Program,是一种不需要下载安装即可使用的应用,它实现了应用“触手可及”的梦想,用户扫一扫或搜一下即可打开应用。

最近两周由于公司业务需求,本胖主导开发了一款功能简单的小程序—A计划(以下本次开发小程序的代号),可以说这两周本胖都是在浏览小程序官网以及小程序论坛,适应小程序的开发模式以及填开发过程中的大坑小。这里需要说一句,自己平时做demo和开发公司级别的正式项目永远都是两码事哈,本胖这次也是深有体会,很多之前做demo时候只是模棱两可的知识在这次开发中得到了深入认识,还有就是对小程序开放能力的深入理解,知道了小程序能做什么,小程序不能做什么,所以敬以此文记录本胖对小程序的认知,如有错误,烦请指出,谢谢。

1 小程序登录以及授权

1.1 小程序登录

任何项目都会涉及到登录这个问题,在JSP的远古时代登录都是直接由服务端控制,在现在react,vue单页面时代,登录是有客户端控制,所以现在的很多前端框架都会有全路由守护这种东西,能很方便地帮助我们控制路由权限,那么在小程序里面的登录时怎么控制的呢?

小程序里面的登录主要分2种

A. 利用现有登录体系

直接复用现有系统的登录体系,只需要在小程序端设计用户名,密码/验证码输入页面,便可以简便的实现登录,只需要保持良好的用户体验即可。
复制代码

B.利用OpenId

OpenId 是一个小程序对于一个用户的标识,利用这一点我们可以轻松的实现一套基于小程序的用户体系,值得一提的是这种用户体系对用户的打扰最低,可以实现静默登录。具体步骤如下:

1.小程序客户端通过 wx.login 获取 code

2.传递 code 向服务端,服务端拿到 code 调用微信登录凭证校验接口,微信服务器返回 openid 和会话密钥 session_key ,此时开发者服务端便可以利用 openid 生成用户入库,再向小程序客户端返回自定义登录态

复制代码

这次由于需要获取用户手机号的,所以我们选择了第二种(注意了,个人主体的小程序号是获取不了用户手机号的)。但是这里需要强调的一点是登录和授权获取用户信息(比如手机号,头像,微信步数)是两回事情,不要搞混淆了,登录是可以做到静默登录的,但是很少有小程序只做一个登录的,那会是一个没有灵魂的小程序哈。

1.2 授权

这里不得不承认,微信是中国流量最大的app,所有小程序才有了生根萌芽的沃土,做小程序就不得不涉及到相关用户权限的授权。A计划小程序这次涉及到授权的地方主要是获取用户手机号,还记得那年夏天微信小程序获取手机号还是可以直接通过API方式直接弹出授权弹框的,这次发现竟然不行了,只好去官网看看改动了哪些(不得不说这也是小程序的不确定性,一家独大,想改就改),发现现在要获取用户手机号只能通过引导用户触发相应的按钮才行。

1.3 登录+授权解决方案

这次本胖采用的方案是专门用一个页面(login)来进行用户的登录以及授权,这里需要注意的是,你在取微信用户绑定的手机号,需先调用wx.login接口。

我们首先需要下面这个button组件用来引导用户触发登录

<button type="primary" open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">微信登录</button>
复制代码

然后需要在login页面的onload事件中发起wx.login(),获取对应的code存起来。 关键的一步到了,在用户点击确定授权的按钮,我们需要监听getPhoneNumber事件

getPhoneNumber(e) {
    if (e.detail.iv) {
      this.setData({
        iv: e.detail.iv,
        encryptedData: e.detail.encryptedData
      });
      this.login(this.getPhone);
    }
}
复制代码

注意到上面最后一行的代码,是在获取到iv以及encryptedData这两个解密用户手机号必备的变量的时候才去发起登录以及获取对应的手机号前端显示用。

可以看到上面的微信登录按钮其实一开始是先做了一个获取用户手机号权限的操作,操作成功后才登录的,如果一开始就登录了,但是用户没有同意授权手机号,那么将变得毫无意义,因为手机号才是小程序需要快捷获取的核心用户信息之一。

登录完成后客户端可以获取到一个自定义token。类似于浏览器里面的JSSSESIONID,可以存小程序本地,在需要的时候手动带上就可以了,这一点比不了浏览器可以设置自动带上cookie那么方便。

2 生成海报

为什么很多小程序里面都会有生成海报呢,其实也是因为小程序不能直接转发到朋友圈,不能直接转发到QQ以及其他社交平台,那么就需要出现海报这个中间传播者。这次A计划小程序里面也有这个功能,于是本胖就愉快地开始了海报的填坑。

小程序的海报一般都是通过canvas生成的,也就是填小程序canvas的各种坑哈。

2.1 实体机海报空白

上图中就是在生成海报的时候出现的实体机空白问题,但是在模拟器里面却不会有,在查阅了很多资料以后发现是图片资源没能及时加载出来,就会造成在drawImg的时候没有真实的图片可以画,当然就是空白了。

既然是因为在画图的时候图片没有及时请求到造成的问题,那么解决的方案当然就是在页面初始化的时候就去异步请求图片

downloadFile(url, name) {
  wx.downloadFile({
    url: url,
    success: function (res) {
   
    }
  });
}
复制代码

2.2 背景图片自适应

canvas是需要设置大小的,否则取他的默认大小。但是canvas的单位是px和小程序标准的单位rpx是不同的。所以我们需要动态给canvas区域设置宽高才能让canvas满满填充整个理想区域。

createNewImg: function (text) {
  wx.createSelectorQuery().select('#canvas-container').boundingClientRect( (rect) => {
      this.setData({
        canvasW: rect.width,
        canvasH: rect.height
      });
      var width = this.data.canvasW,
        height = this.data.canvasH,     
        context = wx.createCanvasContext('mycanvas');
      context.drawImage(this.data.imgUrl1, 0, 0, width, height);
      context.draw();
    }).exec();
  },
复制代码

上面的代码就是用过获取不同屏幕下canvas的包裹元素的宽高然后动态给canvas赋值,然后对应图片的画图也是一个道理,就可以让图片只适应不同屏幕。

2.3 文字居中

好了,画好了主图本胖就要在主图写字了,需求是写一段文字,字数不定,居中显示。

本胖一开始想当然了用了setTextAlign设置了文字的对齐方式,可是一看竟然没有说明用,于是本胖只好想算出所有文字的全部长度,用区域宽度减去文字的长度除以2设置文字的起始X坐标就可以了。

context.fillText(`我是吕胖胖`, (width - context.measureText(`我是吕胖胖`).width) / 2, );
复制代码

上面代码里面的measureText就是解决问题的核心API。

2.4 canvas层级太高 导致ios中海报随着屏幕滑动

因为海报主体使用了fixed布局,所以会被固定在屏幕的中间,在模拟器里面测试没有问题,但是在ios上面会出现手指滑动海报,海报会随着页面滑动,这是不是看起来很滑稽。这种现象是因为canvas这东西在小程序里面地位那是相当的高,下面是小程序对canvas的介绍。

canvas组件是原生组件,他有如下的限制

原生组件的使用限制
由于原生组件脱离在 WebView 渲染流程外,因此在使用时有以下限制:

原生组件的层级是最高的,所以页面中的其他组件无论设置 z-index 为多少,都无法盖在原生组件上。
后插入的原生组件可以覆盖之前的原生组件。
原生组件还无法在 scroll-view、swiper、picker-view、movable-view 中使用。
部分CSS样式无法应用于原生组件,例如:
无法对原生组件设置 CSS 动画
无法定义原生组件为 position: fixed
不能在父级节点使用 overflow: hidden 来裁剪原生组件的显示区域
原生组件的事件监听不能使用 bind:eventname 的写法,只支持 bindeventname。原生组件也不支持 catch 和 capture 的事件绑定方式。
在iOS下,原生组件暂时不支持触摸相关事件。
原生组件会遮挡 vConsole 弹出的调试面板。
在工具上,原生组件是用web组件模拟的,因此很多情况并不能很好的还原真机的表现,建议开发者在使用到原生组件时尽量在真机上进行调试。
复制代码

一开始本胖查阅资源后大部分是设置如下

disable-scroll='true'
复制代码

但是试过后并没有什么软用,最后无意间突然发现了一个好用的属性

catchtouchmove='true'
复制代码

该属性加在海报最外面的view上面。

3 生成小程序码+自定义分析

其实微信小程序本身就自带了数据分析,里面有很多比如统计小程序的访问量,访问分析,用户画像等等。当然了,如果你和本胖一样对这些统计不满足的话,你还可以自己定义一些统计。本胖这次的需求是如下

生成N个小程序码,然后区别不同渠道的流量

3.1 生成多个小程序二维码

目前小程序二维码有二种,一种是普通的正方形的(体验版本就是这种),还有一种是小程序官方为了区别其他二维码而专门设置的圆形的体验码,下面所说的体验码都是圆形的体验码,目前只能通过请求小程序官方接口获取。本胖采用的请求A类接口才获取二维码

https://api.weixin.qq.com/wxa/getwxacode?access_token=ACCESS_TOKEN
复制代码

这个接口需要获取一个叫做access_token的东西

下面是获取access_token的接口

GET https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
复制代码

好了,接下来就是愉快地获取二维码了。这时候又一次见识到小程序坑爹的地方了。

本胖愉快地写了一个本地ajax请求(一定要post),代码如下:

$.ajax({
  method: 'post',
  url: 'https://api.weixin.qq.com/wxa/getwxacode',
  dataType: 'json',
  data: {
    access_token: 本胖的accesss_token
    path: 'pages/index/index?id=1',
  },
  success: (data) => { 
    console.log(data);
  }
});
复制代码

发现报错,参数为空,再去看官方发现有下面的提示

哦哦,原来是要json字符串,而jq默认是contentType是application/x-www-form-urlencoded,于是本胖很快按照提示改成了下面的contentType

contentType: "application/json; charset=utf-8"
复制代码

这会总该可以了吧。

没想到又是接受不到参数。。。。

最后在尝试了很多次以后终于发现了正确的请求方式

$.ajax({
  method: 'post',
  url: '/wxa/getwxacode?access_token=本胖的accesss_token',
  dataType: 'json',
  contentType: 'text/plain;charset=UTF-8',
  data: JSON.stringify({
    path: 'pages/index/index?id=1',
  }),
  success: (data) => { 
    console.log(data);
  }
});
复制代码

有没有很惊喜,有没有很意外。这种设计真的很蛋疼,小程序官方最好给一个请求实例哈。

3.2 自定义分析 好了,经历了千辛万苦的尝试,本胖在上一届总算请求到了A计划的小程序圆形码了。接下来就是做自定义分析。

所谓自定义分析,在小程序后台管理系统是可以找到的,顾名思义就是我们可以自己定义一些用户行为的分析,比如用户点击某个按钮的次数,用户停留在某些页面的时间等等,可以说是小程序官方留给开发者埋点的口子

看了一下关于自定义分析的介绍(文章真的很长很长),知道了自定义分析主要步骤如下

1.新建事件
2.选择配置方式
    A.填写配置:不需要任何小程序代码
    B.API上报:需要加对应的小程序代码
3.保存测试或者发布
复制代码

注意到了在选择配置方式的时候是有2种的,本胖这次需求是要通过不同的小程序码区别不同渠道的流量,所以选择了第二种。

对了,这里需要说明之前在获取小程序码的时候path参数,本胖在路径后面是传了参数的,这个参数就是用来区别不同渠道。

然后配置好后,可以获取到对应的上报代码

wx.reportAnalytics('statistics', {
  id: '',
});
    
复制代码

这里看出本胖是选择了一个名称是statistics的事件名称,然后本胖在A计划小程序的首页的onload事件中加入了以下代码

onLoad: function(options) {
  console.log(options);
  wx.reportAnalytics('statistics', {
    id: options.id
  });
}
复制代码

那么不同渠道的小程序进入就会获取到不同的id上报给小程序平台,在后台管理中根据不同的渠道id的上报次数来判断不同渠道的流量情况

但是,现实有这么美好吗?

在体验版的小程序的路径中加入了不同的id,体验了一把,去小程序的后台查看对应的数据(本胖的自定义事件已经发布),有下面的截图

看的只是不同id的去重总和数据,并没有不同id对应的数据,也就说通过小程序的自定义分析实现不同小程序码区别不同渠道的想法是不行的,该次尝试以失败告终,也让本胖知道了小程序有哪些是还做不了的,所以说这样的尝试也是有意义的哈。

对于这个需求,本胖最终还是打算采用我们自己的服务端来实现不同渠道的流量分析。

4 其他坑点

哈哈,这一节的标题不知道取什么好了,只好取了这个。

4.1 横向滚动

做H5的时候,实现横线滚动那是so easy的事情,大家都懂哈,不就是一个熟悉的问题嘛。 但是,这么简单的需求在小程序里面并没有那么简单。

<view>
  <scroll-view scroll-x>
  </scroll-view>
</view>
复制代码

像上面这样,设置了scroll-x属性就以为当scroll-view 的内容超出scroll-view 时候会出现横向滚动条,但是实际上并不是这样的,还需要下面的设置

scroll-view
  white-space: nowrap;
子元素
  display: inline-block;
复制代码

white-space:nowrap;这个属性在css里面的意思是

规定段落中的文本不进行换行

4.2 inupt动态绑定

尝试过小程序的都发现他的语法和vue很像,所以上手都是很快的。但是当本胖遇到input的时候果断写了下面的代码

<input value="{{name}}">
复制代码

想通过这种方式的绑定来动态获取到输入框输入的值。

但是现实又是残酷的。

人家小程序根本就不支持这种写法。

有没有很坑,都向vue学习了90%的语法了,input绑定变量使用评率这么高的竟然不学。

吐槽归吐槽,活还是要继续干下去的。只好采用react版方式来动态获取input的输入值了。

this.setData({
  val: e.detail.value
});
复制代码

4.3 wx.request

这是小程序用来获取服务端数据的api,使用频率那也是很高的,但是他在本胖看来主要有2处需要注意的地方

1.默认的contentType是application/json,需要自己改写为合适的和服务器端通讯
2.和传统的ajax很像,容易走入回调地狱,可以自己封装成promise形式哈
复制代码

5. 小结

这篇文章主要是总结了本胖这两周在开发A计划小程序遇到的问题以及对应的解决方案和对微信小程序的一些个人看法。

不过,微信小程序的确可以说是前端史上一次重大的突破,其体验,能力(虽然大部分也都是通过使用微信app的能力)都比一般的H5更好更强。而且语法和vue很像,易于上手,开发体验也不错,相应的社区工具都越来越完善了,很期待小程序的未来。

这应该是本胖工作2年多来写的最长的一篇技术博文了,二年多时间说过就过,本胖唯有努力使自己的能力与年龄成正相关才行哈。

鲜花
鲜花
鸡蛋
鸡蛋
分享至 : QQ空间
收藏
原作者: 我叫吕胖胖 来自: 掘金