微信小程序分享功能

目前微信内分享常见三种形式,推荐到好物圈,朋友圈分享,微信好友/群分享。

朋友圈分享,目前来看大多用画图来完成。由于微信没有提供直接分享朋友圈的接口,因此各路神通想出了:生成图片保存相册,转发朋友圈,扫图中小程序码的“三段跳”解决方案。这样在海报样式上能做更大的文章。比如插入用户信息,产品的信息,更酷炫的设计等等。

微信小程序分享功能知识点

最近开发微信小程序,记录一些重要的知识点。部分内容引用自微信官方文档。

概览

概览部分引用自《微信开放文档》

小程序与普通网页开发的区别

网页开发的渲染线程和脚本线程是互斥的,因此长时间运行脚本可能导致页面失去响应。

在小程序中,渲染和脚本分别运行在不同的线程中。

逻辑层运行在 JSCore 中,并没有一个完整浏览器对象,因而缺少相关的 DOM API 和 BOM API。这导致一些常用库(jQuery、 Zepto 等),在小程序中是无法运行的。同时 JSCore 的环境同 NodeJS 环境也是不尽相同,所以一些 NPM 的包在小程序中也是无法运行的1

1. 微信开放文档

小程序运行机制

启动

  • 热启动:假如用户已经打开过某小程序,然后在一定时间内再次打开该小程序,此时无需重新启动,只需将后台态的小程序切换到前台,这个过程就是热启动。
  • 冷启动:用户首次打开或小程序被微信主动销毁后再次打开的情况,此时小程序需要重新加载启动,即冷启动。
  • 小程序没有重启的概念。

前台/后台状态

当用户点击右上角胶囊按钮关闭小程序,或者按了设备 Home 键离开微信时,小程序并没有直接销毁,而是进入了后台状态;

当用户再次进入微信或再次打开小程序,小程序又会从后台进入前台。

小程序销毁

需要注意的是:只有当小程序进入后台一定时间,或者系统资源占用过高,才会被真正的销毁。

  • 当小程序进入后台,客户端会维持一段时间的运行状态,超过一定时间后(目前是 5 分钟)小程序会被微信主动销毁。
    当小程序占用系统资源过高,可能会被系统销毁或被微信客户端主动回收。
  • 在 iOS 上,当微信客户端在一定时间间隔内(目前是 5 秒)连续收到两次及以上系统内存告警时,会主动进行小程序的销毁,并提示用户 「该小程序可能导致微信响应变慢被终止」。
  • 建议小程序在必要时使用 wx.onMemoryWarning 监听内存告警事件,进行必要的内存清理

在调试过程中,安卓小程序在后台显示为与 App 类似的实际 view,也就是说安卓用户可以手动销毁小程序,但在 IOS 系统上,小程序不显示。这一点还有待考证。

WXS 响应事件的动机

由于视图层和逻辑层分开,有频繁用户交互的效果会比较卡顿。需要先将用户在视图层的 event 传进逻辑层,逻辑层处理完成之后通过 setData()再传入视图层。需要两次通信+一次渲染。setData 渲染也会阻塞其它脚本执行,导致了整个用户交互的动画过程会有延迟。

因此 WXS 的使用的思路是减少通信的次数,让部分逻辑代码在视图层运行。

微信分享

需求描述

目前微信内分享常见三种形式,推荐到好物圈,朋友圈分享,微信好友/群分享

好物圈以插件形式存在,下文不具体涉及。

朋友圈分享,目前来看大多用画图来完成。由于微信没有提供直接分享朋友圈的接口,因此各路神通想出了:生成图片保存相册,转发朋友圈,扫图中小程序码的“三段跳”解决方案。这样在海报样式上能做更大的文章。比如插入用户信息,产品的信息,更酷炫的设计等等。

除了朋友圈分享这种方式,微信好友/群内分享也很常见。微信提供了很多 API,其中包括监听页面内转发事件。在转发的过程中默认截取当前页面为转发图。如果想要得到更酷炫的转发图,也需要用到 canvas 画图来生成。

微信在 Page 中提供了 onShareAppMessage 的事件处理函数,监听用户点击页面内转发按钮。该函数返回一个对象,用于自定义转发内容:

1
2
3
4
5
{
title: //标题
path: //转发路径
imgUrl: //自定义图片路径,可以是本地文件路径、代码包文件路径或者网络图片路径
}

因此,自然而然可以想到,在 imgUrl 上做文章。需要动态生成一张图片,并保存其 url。到此,思路便比较清晰了:生成图片可用 canvas 绘图,并利用微信提供的 API - wx.canvasToTempFilePath 得到本地图片 url。

程序流程

分享图生成的画图方法在页面的生命周期中位置如下:
1

页面视图 wxml 文件中,button 绑定 onShareAppMessage()方法。同时设置 canvas 画布。利用 wxss 设置样式移到可见范围之外。

页面初次加载(onLoad)时,获取需要分享信息数据。可能同时包含有文字和图片信息。若图片来自网络,则会先下载到本地为画图备用。需要利用 wx.getImageInfo(),获得该图片的本地 url,在 success 回调函数中进行下一步处理画图。可以将该 API 包进一个 Promise 对象中返回,便于其他模块处理网络图片的链式调用和后续处理。

创建 canvas 上下文,利用 canvas 相关方法画图。关键的 draw()方法有三个重要参数,第一个表示是否保留画布之前内容,第二个为绘制完的回调函数。在回调函数中调用 wx.canvasToTempFilePath(),把当前画布指定区域的内容导出生成指定大小的图片。第三个为需要作用于的 this 对象。这一点很重要,因为需要在 wx.canvasToTempFilePath()成功后,在其 success 回调中,将生成图片的 url 设置为 page 的全局变量。这样能在 page 作用域内,将生成图片的 url 设置到事件处理函数 onShareAppMessage()当中。

FAQs

  1. 为什么不隐藏 canvas,设置 display:hidden

    canvas 标签隐藏会导致绘制失效

  2. 为什么在页面加载时就生成图片,而不是点击分享按钮后生成图片

    在页面事件 onShareAppMessage()运行时,只有按规则返回具有对应参数的对象时,才能成功弹出分享到微信好友界面。(作者尝试通过 Promise 异步返回,但由于 wx 限制,若返回 Promise 对象便不能识别。)然而,在画图过程中调用的几个 API 均为异步方法,因此对于 page 页面的事件,无法做到先点击 share button,触发 onShareAppMessage()后,在该方法内调用其他异步绘图方法。这样无法得到新绘制图的 url。

  3. 关于 2 真的做不到吗?我不是很相信

    具体见 4、同类小程序分享调研

  4. 第一次点分享按钮图片不是我想要的,退出再点击一次就好了

    说明异步画图的 url 还没设置进去,用户就点击了分享按钮。这个时候显示默认的界面截图。退出后再点击的过程中,图片画好了,url 已生成也设置进去了,正常显示。

    为了解决这个问题,可以添加一个 page 全局变量记录画图状态,已画完才显示 button,否则不显示。

  5. 新绘制的图片中可以将多张图片合成在一起吗?

    能,在用户授权后,还可以添加用户头像和昵称信息

同类小程序分享功能调研&猜想

唯品会:有信息,点击分享 button,加载约 1 秒,弹出 Component 组件。在组建内集和好物圈,微信好友,朋友圈三种分享方式。分别可点击,有不同效果。
猜想:虽然不知道源码,但 share-button 基本和其他页面元素一同加载出来。点击 button 后有明显的 1 秒左右加载过程。笔者猜想 canvas 画图便设置在了这段时间内。注意这本质上也是先画图,再分享的过程。但在体验上完美地解决了问题 2 的整套动作。

网易考拉:无信息,只有一个网络图没处理

蘑菇街:有信息,在微信分享弹框内会有“。。。”等待加载。
猜想:有时主图加载时显示为视频,此时若点击过快会出现只有一张图的情况。笔者猜想是由后端生成的图片,直接返回 url。点击过快可能信息还未返回,因此显示默认图片。

京东,拼多多,每日优鲜:有信息,点击过快只有主图
猜想:前端画图,未处理分享按钮,因此若点击过快没画完,则设置为主图。

苏宁易购:类似唯品会的“二级跳”,但是点击一级分享按钮后,过一会才会生成分享微信好友的按钮。这段时间内,大概率在进行 canvas 画图。画完图之后显示分享按钮。

后端方案

保险的做法,不受微信能力的限制。在后端画图,大体上需要提供两个 API。

  • 画图并上传服务器,返回 URL。
  • 通过刚才生成的 URL 拿到图片

存在的问题:后端画图无可厚非,不过在实际过程中,以 Java 为例,画图时间较长。如果加上来回前后端通信的时间,似乎时延是不可忍受的量级。同时由于该需求中需要显示的信息存在过期问题,缓存机制也将需要进一步考虑。

可能存在的 bug

  • 人民币符号‘¥’在 ios 系统中显示可能出现问题

参考文献