在微信小程序里做带参分享 + 转化漏斗
一句话定义:在小程序的
onShareAppMessage/onShareTimeline里把inviter等参数塞进path/query,接收方页面onLoad解析出来落到 Cloudbase 数据库,再用云函数生成带 scene 的 wxacode 海报,组成一条可统计的拉新链路。预计耗时:30 分钟 | 难度:基础
适用场景
- 适用:已经接入 add-auth-wechat-miniprogram,想做邀请奖励 / 拉新分销 / 渠道追踪
- 适用:需要给营销活动生成可大量分发的小程序码海报
- 不适用:跨 App 分享(走不同的 SDK)
- 不适用:H5 网页分享
环境要求
| 依赖 | 版本 |
|---|---|
| 微信基础库(分享到聊天) | ≥ 1.2.4 |
| 微信基础库(分享到朋友圈) | ≥ 2.11.3 |
@cloudbase/js-sdk | 2.27.3 |
wx-server-sdk | latest(用于云调用 wxacode) |
另外需要:
- 已完成 add-auth-wechat-miniprogram 和 add-database-wechat-miniprogram
- 想用
cloud.openapi.wxacode.getUnlimited时,需要小程序和当前 env 已在控制台关联(同 add-subscribe-message-cloud-function)
第一步:分享按钮带参
在要被分享出去的页面里(假设是 pages/product/product.js),实现 onShareAppMessage。
// pages/product/product.js
Page({
data: {
productId: '',
},
onLoad(options) {
this.setData({ productId: options.id });
},
onShareAppMessage(res) {
// res.from: 'menu' (右上角分享) | 'button' (业务按钮)
const inviter = getApp().globalData.user?.customUserId || '';
return {
title: '我刚下单了这个,你也来看看',
path: `/pages/product/product?id=${this.data.productId}&inviter=${inviter}&channel=share`,
imageUrl: '/assets/share-cover.png', // 5:4 PNG/JPG,选填,不传用默认截图
};
},
});
页面布局上,要让用户主动触发,需要在 wxml 里加一个 <button open-type="share"> 业务按钮。
返回值的几个易疏漏点:
path必须以/开头imageUrl不传走默认截屏(微信会截当前可视区域),线上活动页通常自己提供一张固定图,避免截图时机不一致- 想异步组装(比如要 await 一次接口)时,把整个返回值改成
{ title, path, imageUrl, promise: someAsync() }
第二步:接收方解析参数 + 落库
被分享方点开后还是同一个页面,但 onLoad 的 options 会带上分享链接里的 query。
// pages/product/product.js,继续上面那段
import { db, auth } from '../../libs/cloudbase';
import { ensureLogin } from '../../libs/login';
Page({
async onLoad(options) {
this.setData({ productId: options.id });
const inviter = options.inviter;
const channel = options.channel;
if (!inviter) return;
await ensureLogin();
const me = auth.currentUser?.customUserId;
if (!me || me === inviter) {
// 邀请人和被邀请人是同一个,跳过
return;
}
// 落库到 share_events,用 inviter+invitee 复合唯一约束防重
await db.collection('share_events').add({
inviter,
invitee: me,
productId: options.id,
channel,
enteredAt: db.serverDate(),
});
},
});
注意:
share_events集合的权限模式建议设为「仅创建者可读写」,被邀请方写自己的记录,_openid自动带上- 重复进入应避免重复落库。最简洁的做法是云函数侧拿
(inviter, invitee, productId)做唯一索引,前端只管发,后端去重 - 如果做奖励发放,落库不等于发放,发放走云函数 + 内部状态机,不要直接信前端
第三步:朋友圈分享
朋友圈分享的接口是 onShareTimeline,基础库 ≥ 2.11.3 才支持,且 query 限制比群分享严:
Page({
onShareTimeline() {
const inviter = getApp().globalData.user?.customUserId || '';
return {
title: '我刚下单了这个,你也来看看',
query: `id=${this.data.productId}&inviter=${inviter}`,
imageUrl: '/assets/share-square.png', // 1:1 PNG/JPG
};
},
});
差异点:
- 朋友圈分享不能改
path,只能用「当前页面 + query」,所以接收方拿到的还是当前页 query不要带&channel=share这种业务标识也行,但建议加,后面统计要用- 用户的微信版本如果低于 7.0.15 / 基础库低于 2.11.3,微信里直接看不到「分享到朋友圈」按钮,这时分享方的
onShareTimeline不会被调用,业务上可以不显式判断 - 朋友圈打开是一个「单页模式」,部分 wx API 不可用,不要在落库逻辑里依赖这些 API
第四步:云函数生成带参 wxacode 海报
线下场景或者批量推广时,需要给每个邀请人生成一张带 inviter 的小程序码。云函数走云调用直接拿:
cloudfunctions/genWxacode/index.js:
const cloud = require('wx-server-sdk');
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV });
exports.main = async (event) => {
const { inviter, productId } = event;
// scene 最长 32 字符,只能是 ASCII 可见字符,所以一般用紧凑 key=value 格式
const scene = `i=${inviter}&p=${productId}`;
if (scene.length > 32) {
return { ok: false, error: 'SCENE_TOO_LONG', scene };
}
try {
const res = await cloud.openapi.wxacode.getUnlimited({
scene,
page: 'pages/product/product',
checkPath: false, // 联调期为 true 容易因为页面没发布而报错
envVersion: 'release', // release / trial / develop
width: 430,
});
// res.buffer 是 PNG 二进制
const upload = await cloud.uploadFile({
cloudPath: `wxacodes/${inviter}-${Date.now()}.png`,
fileContent: res.buffer,
});
return { ok: true, fileID: upload.fileID };
} catch (err) {
return { ok: false, errcode: err.errCode, errmsg: err.errMsg };
}
};
接收方页面里要做的额外一步:小程序通过扫描码进入时,onLoad 拿到的不是 options.id 之类的 query,而是 options.scene,需要解码:
onLoad(options) {
let inviter = options.inviter;
let productId = options.id;
// 扫码进入,scene 是 URL encode 过的 'i=xxx&p=yyy'
if (!inviter && options.scene) {
const decoded = decodeURIComponent(options.scene);
const params = Object.fromEntries(
decoded.split('&').map((kv) => kv.split('=')),
);
inviter = params.i;
productId = params.p;
}
// 后续逻辑同第二步
}
要点:
scene上限 32 字符,且只支持 ASCII 可见字符,长 openid(28 位)塞进去基本没空间放别的,推荐用userIndex(数据库里另一张映射表)做 短码getUnlimited没有数量上限,适合大批量;另一个接口wxacode.get单小程序总额度 10 万张,慎用checkPath在联调阶段建议设false,等页面发布后再开
第五步:转化漏斗查询
数据落到了 share_events,要看转化拿 db.command 聚合一下:
// 控制台 → 数据库 → share_events → 数据查询(高级模式)
db.collection('share_events')
.aggregate()
.group({
_id: '$inviter',
invitees: $.addToSet('$invitee'),
count: $.sum(1),
})
.sort({ count: -1 })
.limit(20)
.end();
或者在云函数里跑日报,见 schedule-cloud-function-cron-job。
运行验证
- 在微信开发者工具里,产品详情页右上角 → 转发给「文件传输助手」
- 用第二个测试账号点开链接,Console 应该看到
inviter参数被解析 - 控制台 → 数据库 → share_events,有一条
inviter=A、invitee=B 的记录 - 朋友圈分享要在真机上才能正常验证,开发者工具不支持
- 调一次
genWxacode云函数,在wxacodes/目录看到生成的 PNG,扫码后能进对应商品页
常见错误
| 错误现象 | 原因 | 修复 |
|---|---|---|
| 转发后参数全部丢失 | path 没以 / 开头 | 改成 /pages/... |
| 朋友圈分享不弹按钮 | 没实现 onShareTimeline 或基础库版本太低 | 实现 onShareTimeline,在 app.json 里把 minimum 提到 2.11.3 |
wxacode.getUnlimited 报 41030 | page 路径在已发布版本里不存在 | 把 checkPath 改 false 或先发布一次小程序 |
wxacode 报 scene 不合法 | scene 里有中文或特殊字符 | 改用纯 ASCII,中文字段先存数据库,scene 只放短 ID |
| 同一用户进入多次重复落库 | 没有去重 | 在 share_events 加复合唯一索引 inviter+invitee+productId,或者云函数 set({ ... }, { merge: true }) |
相关文档
- Page.onShareAppMessage — 分享回调结构
- Page.onShareTimeline — 朋友圈分享
- wxacode.getUnlimited — 不限量小程序码
- add-database-wechat-miniprogram — 前置:数据库读写
- add-auth-wechat-miniprogram — 前置:登录接入
下一步
- 邀请奖励的发放流程:add-subscribe-message-cloud-function
- 转化日报定时跑:schedule-cloud-function-cron-job
- 多租户场景的分享隔离:secure-database-multi-tenant-rules