做 UniApp 开发微信小程序,分享功能绝对是刚需。不管是拉新裂变、商品推广还是活动传播,分享给微信好友、转发朋友圈都是核心流量入口。
不少同行开发时踩过坑:朋友圈分享入口莫名消失、分享图片加载不出来、参数传递丢失、海报生成后保存失败…… 今天结合日常开发实战,把 UniApp 小程序全场景分享方案梳理清楚,从基础配置、多端适配、海报制作到问题排查,代码均可直接复制上线,新手也能快速上手。
一、先分清:分享好友和朋友圈的核心区别
很多人刚接触时会把两种分享混为一谈,其实二者调用 API、规则限制差异很大,先理清楚底层逻辑,后续开发会少走一半弯路。
朋友圈分享无法自定义页面,参数还受限,这是微信平台硬性规则,开发时一定要提前规避。
二、基础配置:开启小程序分享菜单
默认状态下,小程序右上角是看不到「分享到朋友圈」入口的,必须手动开启分享菜单,这是所有分享功能的前置操作,缺一不可。
这里提供两种配置方式,按需选择即可:
方式 1:页面内动态开启(推荐,灵活可控)
在页面onLoad生命周期中调用uni.showShareMenu,同时开启好友分享和朋友圈分享两个入口:
onLoad() {// 开启分享菜单,同时显示好友、朋友圈入口uni.showShareMenu({withShareTicket: true,menus: ['shareAppMessage', 'shareTimeline']})}
方式 2:全局静态配置(全页面生效)
如果项目所有页面都需要分享能力,直接在pages.json中全局配置,不用逐个页面写代码:
{"globalStyle": {"enableShareMenu": true}}
实战踩坑提醒:只写
pages.json配置,部分机型仍会隐藏朋友圈入口,优先使用uni.showShareMenu动态调用,兼容性更稳。
三、分享给好友(onShareAppMessage)实战开发
这是最常用的分享场景,支持自定义标题、跳转页面、配图,还能正常传递业务参数,下面从基础用法、动态渲染、参数传递逐一讲解。
1. 基础通用写法
适配微信小程序环境,用条件编译区分端环境,避免其他端报错:
export default {data() {return {productId: '',productName: ''}},onLoad(options) {// 接收上个页面传递的参数this.productId = options.id || ''this.productName = options.name || '精选好物'},// 仅微信小程序生效// #ifdef MP-WEIXINonShareAppMessage(res) {return {title: `${this.productName} - 限时特惠,速来抢购`,// 自定义分享跳转页面+参数path: `/pages/index/index?id=${this.productId}`,// 分享配图(建议5:4比例,大小控制在500KB以内)imageUrl: 'https://xxx.com/static/share-card.png'}}// #endif}
2. 动态渲染分享内容(商品详情页常用)
电商类小程序基本都需要根据当前商品,动态变更分享标题、图片,示例如下:
// 商品详情页分享逻辑onShareAppMessage(res) {const goods = this.currentGoodreturn {// 优先使用商品名称,无数据则展示默认文案title: goods.goods_name || '发现一款宝藏好物',// 追加来源标记,用于统计分享渠道path: `/pages/goods/detail?id=${goods.id}&from=share`,// 动态加载商品主图作为分享图imageUrl: goods.goods_image || '/static/default-share.png'}}
3. 分享参数传递实操
分享链接携带参数,是实现邀请裂变、渠道统计的关键。
发送方:在 path中拼接参数
onShareAppMessage() {return {title: '邀请好友领福利',path: '/pages/invite/index?scene=invite001&from=share',imageUrl: 'https://xxx.com/invite.png'}}
接收方:在页面onLoad中直接解析获取
onLoad(query) {console.log('分享携带参数:', query)// 打印结果:{ scene: 'invite001', from: 'share' }}
如果参数过长容易被截断,建议用 Base64 编码,或者让后端生成短链接映射,稳定性更高。
四、分享到朋友圈(onShareTimeline)实战开发
朋友圈分享规则限制最多,也是问题高发区,核心记住两点:只能分享当前页、query 参数≤20 字符。
1. 基础写法
// #ifdef MP-WEIXINonShareTimeline() {return {title: '超多优惠好物,点击查看',// 仅支持query传参,不要拼接pathquery: 'id=123&from=timeline',// 必须使用正方形图片imageUrl: 'https://xxx.com/poster-square.png'}}// #endif
2. 动态适配 + 参数精简
商品页分享朋友圈时,一定要精简参数,避免超出字符限制:
onShareTimeline() {const goods = this.currentGood// 精简参数,控制在20字符内const queryStr = `id=${goods.id}&from=tl`return {title: goods.goods_name,query: queryStr,imageUrl: goods.share_poster || '/static/default-poster.png'}}
朋友圈分享硬性限制汇总
页面固定:无法自定义跳转路径,用户分享哪个页面,点击就打开哪个页面; 参数限制:query 字符串最大 20 个字符,超长必被截断; 图片规则:单张正方形图片,尺寸建议 200×200px 以上,非正方形会被系统强制裁剪; 域名校验:图片链接必须在微信公众平台配置合法 HTTPS 域名,否则图片不显示。
五、双端合一:同时支持好友 + 朋友圈分享完整模板
日常开发中,一个页面基本都需要同时支持两种分享,这里整理好可直接套用的完整模板,适配绝大多数页面:
export default {data() {return {shareId: '',shareTitle: '默认分享标题'}},onLoad(options) {this.shareId = options.id || '0'this.shareTitle = options.title || '精选内容'// 统一开启分享菜单uni.showShareMenu({withShareTicket: true,menus: ['shareAppMessage', 'shareTimeline']})},// #ifdef MP-WEIXIN// 分享好友onShareAppMessage(res) {return {title: this.shareTitle,path: `/pages/index/index?id=${this.shareId}&from=share`,imageUrl: '/static/share-friend.png'}},// 分享朋友圈onShareTimeline() {return {title: this.shareTitle + ' - 限时优惠',query: `id=${this.shareId}`,imageUrl: '/static/share-timeline.png'}}// #endif}
六、拓展能力:App 端、H5 端分享方案
UniApp 是多端框架,除了微信小程序,App、H5 端也会用到分享功能,分开说明:
1. App 端多平台分享(微信 / QQ / 微博)
App 端使用uni.share调用原生分享能力,支持多平台分发,条件编译限定仅 App 端生效:
// #ifdef APP-PLUSuni.share({provider: 'weixin', // 分享至微信type: 5, // 5代表分享小程序卡片title: '实用小程序推荐',scene: 'WXSceneSession', // WXSceneSession=好友 WXSceneTimeline=朋友圈miniProgram: {id: 'gh_xxxxxxx', // 小程序原始IDpath: '/pages/index/index',type: 0, // 0正式版、1开发版、2体验版webUrl: 'https://xxx.com/h5-page' // 非微信环境兜底H5页面},success: () => {uni.showToast({ title: '分享成功' })},fail: (err) => {console.log('分享失败:', err)}})// #endif
2. 系统原生分享面板(全 App 通用)
不想区分平台时,调用系统自带分享面板,适配所有手机机型:
// #ifdef APP-PLUSuni.shareWithSystem({title: '分享标题',summary: '内容简介',href: 'https://xxx.com',imageUrl: '/static/share.png',success: () => uni.showToast({ title: '分享成功' })})// #endif
3. H5 端微信分享(依赖 JSSDK)
H5 页面在微信内分享,无法使用 UniApp 原生 API,必须接入微信 JSSDK,步骤如下:
在 public/index.html引入微信 JS 文件:
<scriptsrc="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>前端配置签名与分享内容:
// #ifdef H5import wx from 'weixin-js-sdk'export default {onLoad() {this.initWxShare()},methods: {async initWxShare() {// 1. 向后端请求微信签名(核心,签名错误直接分享失效)const signData = await this.$api.getWxSign()// 2. 初始化JSSDKwx.config({debug: false,appId: signData.appId,timestamp: signData.timestamp,nonceStr: signData.nonceStr,signature: signData.signature,jsApiList: ['updateAppMessageShareData', 'updateTimelineShareData']})wx.ready(() => {// 分享好友wx.updateAppMessageShareData({title: 'H5分享标题',desc: '内容描述',link: window.location.href,imgUrl: 'https://xxx.com/share.png'})// 分享朋友圈wx.updateTimelineShareData({title: '朋友圈标题',link: window.location.href,imgUrl: 'https://xxx.com/share.png'})})}}}// #endif
H5 分享必须在微信公众平台配置JS 安全域名,页面 URL 必须和域名保持一致,否则签名失效。
4. 按钮触发分享
除了右上角胶囊按钮,也可以自定义页面按钮触发分享,使用open-type="share"即可:
<template><buttonopen-type="share">点击分享给好友</button></template>
// #ifdef MP-WEIXINonShareAppMessage(res) {// 区分触发来源:menu(右上角菜单)、button(页面按钮)if (res.from === 'button') {console.log('由页面按钮触发分享')}return {title: '自定义分享内容',path: '/pages/index/index',imageUrl: '/static/share.png'}}// #endif
七、高阶玩法:Canvas 生成带小程序码海报
朋友圈分享卡片不支持直接展示小程序码,想要实现「保存海报→扫码进小程序」的裂变玩法,就需要用 Canvas 绘制海报,搭配 UniCloud 云函数获取小程序码,整套流程完整落地如下。
整体流程:
用户点击生成海报 → 云函数获取小程序码 → Canvas 绘制图文海报 → 保存海报到相册
步骤 1:编写云函数(获取微信小程序码)
新建云函数getQRCode,调用微信官方接口生成无限次小程序码:
'use strict';const crypto = require('crypto')exports.main = async (event, context) => {const { scene, page, width, env_version } = eventconst accessToken = await getAccessToken()const apiUrl = `https://api.weixin.qq.com/wxa/getUnlimited?access_token=${accessToken}`const res = await uniCloud.httpclient.request(apiUrl, {method: 'POST',data: {scene: scene || 'from=poster',page: page || 'pages/index/index',width: width || 280,auto_color: false,env_version: env_version || 'release',is_hyaline: true // 透明背景},dataType: 'json'})return res.data.buffer || res.data}// 缓存access_token(避免频繁请求接口)async function getAccessToken() {const db = uniCloud.database()const config = db.collection('mp-config')const { data } = await config.where({ key: 'access_token' }).get()const record = data[0]// 校验token是否过期if (record && record.expire_time > Date.now()) {return record.value}// 重新获取tokenconst res = await uniCloud.httpclient.request(`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=你的小程序APPID&secret=你的小程序密钥`)const token = res.data.access_token// 存入数据库缓存(提前5分钟过期)if (record) {await config.doc(record._id).update({value: token,expire_time: Date.now() + 7100 * 1000})} else {await config.add({key: 'access_token',value: token,expire_time: Date.now() + 7100 * 1000})}return token}
步骤 2:前端绘制海报 + 保存到相册
包含 Buffer 转临时文件、Canvas 绘图、相册权限校验三大核心逻辑,代码可直接复用:
<template><view><!-- 隐藏画布,用于绘制海报 --><canvascanvas-id="posterCanvas"style="position:fixed;left:-9999px;width:600px;height:900px;"></canvas><button @click="generatePoster">生成分享海报</button><!-- 海报预览 --><viewv-if="posterPath"><image:src="posterPath"mode="aspectFit"></image><button @click="savePoster">保存到相册</button></view></view></template>
export default {data() {return {goodsId: '1001',goodsName: '爆款商品',goodsPrice: 99,posterPath: '',localImagePath: ''}},methods: {// 生成海报主逻辑async generatePoster() {uni.showLoading({ title: '海报生成中...' })try {// 1. 调用云函数获取小程序码const qrRes = await uniCloud.callFunction({name: 'getQRCode',data: {scene: `id=${this.goodsId}`,page: 'pages/goods/detail',width: 280}})// 2. Buffer转为本地临时文件const qrPath = await this.bufferToTempFile(qrRes.result)// 3. 绘制海报await this.drawPoster(qrPath)} catch (e) {uni.showModal({ title: '生成失败', content: e.message })} finally {uni.hideLoading()}},// Buffer转临时文件bufferToTempFile(buffer) {return new Promise((resolve, reject) => {const fs = uni.getFileSystemManager()const fileName = `${Date.now()}_qr.png`const filePath = `${uni.env.USER_DATA_PATH}/${fileName}`fs.writeFile({filePath,data: buffer,encoding: 'base64',success: () => resolve(filePath),fail: reject})})},// Canvas绘制海报drawPoster(qrcodePath) {return new Promise((resolve) => {const ctx = uni.createCanvasContext('posterCanvas', this)// 绘制白色背景ctx.fillStyle = '#FFFFFF'ctx.fillRect(0, 0, 600, 900)// 绘制商品图、文字、小程序码ctx.setFontSize(32)ctx.setFillStyle('#333')ctx.fillText(this.goodsName, 30, 510)ctx.setFontSize(48)ctx.setFillStyle('#FF4A00')ctx.fillText(`¥${this.goodsPrice}`, 30, 600)ctx.drawImage(qrcodePath, 200, 650, 200, 200)ctx.setFontSize(24)ctx.setFillStyle('#999')ctx.textAlign = 'center'ctx.fillText('长按识别小程序码', 300, 900)ctx.draw(false, () => {// 导出海报图片uni.canvasToTempFilePath({canvasId: 'posterCanvas',success: (res) => {this.posterPath = res.tempFilePathresolve()}})})})},// 保存海报到相册(权限处理)async savePoster() {const setting = await uni.getSetting()// 未授权相册权限,引导用户开启if (!setting.authSetting['scope.writePhotosAlbum']) {try {await uni.authorize({ scope: 'scope.writePhotosAlbum' })} catch () {const modalRes = await uni.showModal({title: '权限提示',content: '需要相册权限才能保存图片',confirmText: '去授权'})if (modalRes.confirm) {await uni.openSetting()}return}}// 保存图片await uni.saveImageToPhotosAlbum({filePath: this.posterPath,success: () => uni.showToast({ title: '已保存到相册' }),fail: () => uni.showToast({ title: '保存失败', icon: 'none' })})}}}
八、分享数据统计 + 防刷机制
做运营离不开数据统计,我们可以通过云函数实现分享埋点,同时增加防刷规则,避免恶意刷分享数据。
1. 前端埋点上报
在分享触发时,上报分享类型、页面、用户 ID 等信息:
// #ifdef MP-WEIXINonShareAppMessage(res) {// 分享好友前上报数据this.reportShare('friend', {page: '/pages/index/index',goodsId: this.goodsId,shareFrom: res.from})return {title: this.shareTitle,path: `/pages/index/index?id=${this.goodsId}`,imageUrl: this.shareImage}},onShareTimeline() {// 分享朋友圈上报数据this.reportShare('timeline', {page: '/pages/index/index',goodsId: this.goodsId})return {title: this.shareTitle,query: `id=${this.goodsId}`}},methods: {async reportShare(type, data) {try {await uniCloud.callFunction({name: 'reportShare',data: {type,...data,uid: uni.getStorageSync('userId'),shareTime: Date.now()}})} catch (e) {console.log('分享上报失败:', e)}}}// #endif
2. 云函数记录数据 + 防刷
'use strict';exports.main = async (event, context) => {const db = uniCloud.database()const shareLog = db.collection('share-log')const uid = event.uidconst now = Date.now()const oneHourAgo = now - 3600 * 1000// 防刷规则:1小时内单个用户分享上限50次const count = await shareLog.where({uid,shareTime: db.command.gte(oneHourAgo)}).count()if (count.total > 50) {return { success: false, msg: '分享过于频繁,请稍后再试' }}// 写入分享日志await shareLog.add({uid: event.uid,type: event.type,page: event.page,goodsId: event.goodsId,shareTime: event.shareTime,createTime: now})return { success: true }}
九、高频问题排查
整理日常开发中遇到最多的问题,对应原因和解决方案,遇到问题直接对照排查:
uni.showShareMenu | onLoad中主动调用,配置menus参数 | |
destWidth/destHeight放大 2-3 倍 | ||
十、总结
UniApp 多端分享看着功能繁杂,其实梳理清楚规则后并不难。核心逻辑再精简一遍:
微信小程序分享: onShareAppMessage(好友)+onShareTimeline(朋友圈),必须先开启分享菜单;朋友圈牢记两大限制:不可改路径、query 参数≤20 字符; 海报裂变方案:云函数拿小程序码 + Canvas 绘图 + 相册权限校验; 多端适配:App 用 uni.share,H5 依赖微信 JSSDK;上线前必查:域名白名单、图片格式、参数长度三大关键点。
夜雨聆风