微信最近放了个新能力:小程序里点一下,直接拉起公众号的贴图发表页,标题和正文帮你预填好,用户补两张图就能发。发的贴图带"来自小程序"的小尾巴,别人点一下能跳回你的小程序。
官方接口叫 wx.shareToOfficialAccount,基础库 3.9.2 起支持。文档三行就写完了,我把它接进「厨菜记」的菜谱详情页和菜谱弹窗时,实打实撞到三个坑,逐个记一下,给准备接的人省点时间。
坑一:只能被用户 TAP 手势直接触发,异步链一长就报错
这是最容易被文档忽略的约束。它和 wx.showShareImageMenu 一样,属于"必须由用户点击手势发起"的接口,如果你在前面套了 await 拿数据再调用,很可能直接抛:
sql
can only be invoked by user TAP
我一开始想"点按钮 → 异步拉一下最新菜谱详情 → 再拉起发表页",链路里那个 await 就把 TAP 上下文弄丢了。最后的解法是:点击时页面 data 里已经有菜谱数据了,就不再 await,直接用现有数据同步拼装后调用。真要在调用前请求接口,也得确保那次请求在用户点击之前就已经完成、结果缓存在了本地。
另外低版本要兜底,我用 canIUse 挡一下,不支持就 toast:
js
if (!wx.canIUse('shareToOfficialAccount')) {
wx.$toast('当前微信版本不支持,升级后可用')
return
}
入口按钮的显隐也建议在 onLoad 里就算好,别等用户点到不支持的版本才提示:
js
this.setData({ canPostOfficial: wx.canIUse('shareToOfficialAccount') })
坑二:images 只收本地/临时路径,网络图要先转一道
预填配图传的是 images 数组,但它不吃你 OSS 上的网络图片 URL,只接受本地路径或临时路径。你直接把菜谱封面(一个 https 地址)塞进去,贴图页里那张图就是空的。
得先用 wx.getImageInfo 把网络图下成临时文件再传:
js
// 网络图片转本地临时路径
_getShareImage(src) {
return new Promise(resolve => {
wx.getImageInfo({
src,
success: ({ path }) => resolve([path]),
fail: () => resolve([]),
})
})
}
注意两个细节:一是转换本身是异步的,但它是"取图"不是"调接口",实测在 TAP 后 await 这一步再调 shareToOfficialAccount 不会丢手势(丢手势的是坑二里那种走网络的长链路);二是转换失败一定要 resolve([]) 而不是 reject,没有图不该阻断发表,用户进发表页自己也能补图。
坑三:fail 回调不代表失败,用户主动退出也走 fail
这条坑得单独拎出来,因为它直接决定你的埋点和提示写不写错。我查了文档和实测确认:用户在贴图发表页点了"取消/退出",走的也是 fail 回调,而不是单独给一个"用户取消"的状态。
所以千万别在 fail 里弹"分享失败,请重试"------用户明明是自己关掉的,你弹个红色失败给他,体验很怪。我的处理是 fail 只记日志、不提示:
js
wx.shareToOfficialAccount({
title: `今天做了一道「${name}」,来看看我的做法`,
content: lines.join('\n'),
tags: ['来微信做个小程序', '厨菜记'],
images,
success: (res) => {
// res 里有 status 和 postUrl
wx.$toast('感谢分享')
},
fail: (err) => {
// 用户主动退出发表页也会走这里,不能当错误提示
print('贴图发表取消或失败', err)
},
})
success 回调返回 status 和 postUrl,想做发表效果统计的话 postUrl 是唯一能拿到的抓手(但拿不到曝光/点赞这类后置数据,平台侧的)。
参数签名顺手记一下
省得别人再翻文档:
| 参数 | 说明 |
|---|---|
title |
必填,预填到贴图标题 |
content |
预填正文文字 |
tags |
字符串数组,话题标签,上限 10 个 |
images |
本地/临时路径数组,网络图先转 |
recommendPath / recommendTitle |
3.16.1+,贴图里的小程序链接卡片 |
tags 这个字段是我这次参加 #来微信做个小程序 活动的关键------把它写成 ['来微信做个小程序', '厨菜记'],用户发布时话题标签已经带上了,参与活动的判定不用他手动加。
我是怎么让它不散落在各个页面的
厨菜记里需要"发贴图"的不止详情页,菜谱弹窗也要。如果每个入口复制一遍拼装逻辑,将来文案格式一改就得满仓库找。
我把整个能力封进了 sharePageBehavior 的一个 _postToOfficial({ name, foods, seasonings, steps, remark, cover }) 方法里,输入是菜谱的结构化数据,输出是一次拉起:
js
async _postToOfficial({ name, foods = [], seasonings = [], steps = [], remark = '', cover = '' }) {
if (!wx.canIUse('shareToOfficialAccount')) { /* 兜底 */ }
const lines = []
if (foods.length) lines.push(`食材:${foods.map(f => [f.name, f.amount].filter(Boolean).join(' ')).join('、')}`)
if (seasonings.length) lines.push(`调料:${seasonings.map(s => [s.name, s.amount].filter(Boolean).join(' ')).join('、')}`)
if (steps.length) lines.push('做法:', ...steps.map((s, i) => `${i + 1}. ${s}`))
if (remark) lines.push(`心得:${remark}`)
lines.push('', '厨菜记 - 记录你的每一道菜')
const images = cover ? await this._getShareImage(cover) : []
wx.shareToOfficialAccount({ /* ... */ })
}
页面只要把 data 里的菜谱对象拆几个字段传进来就行。
有个小技巧值得单独说:菜谱弹窗是个 Component() 不是 Page(),而 Behavior() 同样能被 Component() 通过 behaviors: [sharePageBehavior] 混入。所以弹窗组件直接混入这个 behavior 就复用到了 _postToOfficial,宿主页面一行代码都不用改------这也符合"分享/传播能力内聚在组件里"的思路,不用要求每个用它的页面各自实现一遍。
小结
接入本身不难,难的是文档把三个关键约束藏得太深:
- TAP 手势 ------前面别挂会走网络的
await - images 只收本地/临时路径 ------网络图过一道
wx.getImageInfo - fail ≠ 失败------用户退出也走 fail,别弹错误提示
这三条踩过一遍,剩下的就是把文案和话题标签拼好了。
如果你的小程序也有"做完一件事想晒一下"的场景------做完一道菜、记完一次跑步、生成一张图------这个接口比传统的"生成海报再让用户去公众号手动发"少好几步,值得接。我这边菜谱详情页和弹窗两个入口都上了,真机验证过能正常拉起发表页。
厨菜记就是这次接口的载体,一个只装你自己家菜谱的微信小程序,微信搜「厨菜记」就能用,免费没广告。有接入上的问题评论区聊,我看到都会回。
