乐于分享
好东西不私藏

UniApp 小程序分享 好友、朋友圈、海报生成(可直接复用代码)

UniApp 小程序分享 好友、朋友圈、海报生成(可直接复用代码)

        做 UniApp 开发微信小程序,分享功能绝对是刚需。不管是拉新裂变、商品推广还是活动传播,分享给微信好友、转发朋友圈都是核心流量入口。

        不少同行开发时踩过坑:朋友圈分享入口莫名消失、分享图片加载不出来、参数传递丢失、海报生成后保存失败…… 今天结合日常开发实战,把 UniApp 小程序全场景分享方案梳理清楚,从基础配置、多端适配、海报制作到问题排查,代码均可直接复制上线,新手也能快速上手。

一、先分清:分享好友和朋友圈的核心区别

        很多人刚接触时会把两种分享混为一谈,其实二者调用 API、规则限制差异很大,先理清楚底层逻辑,后续开发会少走一半弯路。

对比项
分享给微信好友
分享到朋友圈
对应 API
onShareAppMessage
onShareTimeline
触发场景
用户点击「转发给好友」按钮
用户点击「分享到朋友圈」按钮
卡片样式
支持 5:4 比例 / 正方形方图
固定正方形海报样式
小程序码
不展示小程序码
支持展示小程序码
页面路径
可自定义跳转任意页面
仅能分享当前页面,无法修改路径
参数传递
通过 path 拼接参数,无严格长度限制
依靠 query 传参,限制 20 个字符以内
图片要求
最小 200px,PNG/JPG 格式均可
建议 200×200px 以上,比例错乱会被裁剪

        朋友圈分享无法自定义页面,参数还受限,这是微信平台硬性规则,开发时一定要提前规避。

二、基础配置:开启小程序分享菜单

        默认状态下,小程序右上角是看不到「分享到朋友圈」入口的,必须手动开启分享菜单,这是所有分享功能的前置操作,缺一不可。

        这里提供两种配置方式,按需选择即可:

方式 1:页面内动态开启(推荐,灵活可控)

        在页面onLoad生命周期中调用uni.showShareMenu,同时开启好友分享和朋友圈分享两个入口:

onLoad() {  // 开启分享菜单,同时显示好友、朋友圈入口  uni.showShareMenu({    withShareTickettrue,    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-WEIXIN  onShareAppMessage(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.currentGood  return {    // 优先使用商品名称,无数据则展示默认文案    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传参,不要拼接path    query'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'  }}

朋友圈分享硬性限制汇总

  1. 页面固定:无法自定义跳转路径,用户分享哪个页面,点击就打开哪个页面;
  2. 参数限制:query 字符串最大 20 个字符,超长必被截断;
  3. 图片规则:单张正方形图片,尺寸建议 200×200px 以上,非正方形会被系统强制裁剪;
  4. 域名校验:图片链接必须在微信公众平台配置合法 HTTPS 域名,否则图片不显示。

五、双端合一:同时支持好友 + 朋友圈分享完整模板

        日常开发中,一个页面基本都需要同时支持两种分享,这里整理好可直接套用的完整模板,适配绝大多数页面:

export default {  data() {    return {      shareId'',      shareTitle'默认分享标题'    }  },  onLoad(options) {    this.shareId = options.id || '0'    this.shareTitle = options.title || '精选内容'    // 统一开启分享菜单    uni.showShareMenu({      withShareTickettrue,      menus: ['shareAppMessage''shareTimeline']    })  },  // #ifdef MP-WEIXIN  // 分享好友  onShareAppMessage(res) {    return {      titlethis.shareTitle,      path`/pages/index/index?id=${this.shareId}&from=share`,      imageUrl'/static/share-friend.png'    }  },  // 分享朋友圈  onShareTimeline() {    return {      titlethis.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', // 分享至微信  type5, // 5代表分享小程序卡片  title'实用小程序推荐',  scene'WXSceneSession', // WXSceneSession=好友  WXSceneTimeline=朋友圈  miniProgram: {    id'gh_xxxxxxx', // 小程序原始ID    path'/pages/index/index',    type0, // 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,步骤如下:

  1. 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. 初始化JSSDK      wx.config({        debugfalse,        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 } = event  const 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_colorfalse,      env_version: env_version || 'release',      is_hyalinetrue // 透明背景    },    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  }  // 重新获取token  const 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_timeDate.now() + 7100 * 1000    })  } else {    await config.add({      key'access_token',      value: token,      expire_timeDate.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'爆款商品',      goodsPrice99,      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',            width280          }        })        // 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(00600900)        // 绘制商品图、文字、小程序码        ctx.setFontSize(32)        ctx.setFillStyle('#333')        ctx.fillText(this.goodsName30510)        ctx.setFontSize(48)        ctx.setFillStyle('#FF4A00')        ctx.fillText(${this.goodsPrice}`30600)        ctx.drawImage(qrcodePath, 200650200200)        ctx.setFontSize(24)        ctx.setFillStyle('#999')        ctx.textAlign = 'center'        ctx.fillText('长按识别小程序码'300900)        ctx.draw(false() => {          // 导出海报图片          uni.canvasToTempFilePath({            canvasId'posterCanvas',            success(res) => {              this.posterPath = res.tempFilePath              resolve()            }          })        })      })    },    // 保存海报到相册(权限处理)    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({        filePaththis.posterPath,        success() => uni.showToast({ title'已保存到相册' }),        fail() => uni.showToast({ title'保存失败'icon'none' })      })    }  }}

八、分享数据统计 + 防刷机制

        做运营离不开数据统计,我们可以通过云函数实现分享埋点,同时增加防刷规则,避免恶意刷分享数据。

1. 前端埋点上报

        在分享触发时,上报分享类型、页面、用户 ID 等信息:

// #ifdef MP-WEIXINonShareAppMessage(res) {  // 分享好友前上报数据  this.reportShare('friend', {    page'/pages/index/index',    goodsIdthis.goodsId,    shareFrom: res.from  })  return {    titlethis.shareTitle,    path`/pages/index/index?id=${this.goodsId}`,    imageUrlthis.shareImage  }},onShareTimeline() {  // 分享朋友圈上报数据  this.reportShare('timeline', {    page'/pages/index/index',    goodsIdthis.goodsId  })  return {    titlethis.shareTitle,    query`id=${this.goodsId}`  }},methods: {  async reportShare(type, data) {    try {      await uniCloud.callFunction({        name'reportShare',        data: {          type,          ...data,          uid: uni.getStorageSync('userId'),          shareTimeDate.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.uid  const 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 { successfalsemsg'分享过于频繁,请稍后再试' }  }  // 写入分享日志  await shareLog.add({    uid: event.uid,    type: event.type,    page: event.page,    goodsId: event.goodsId,    shareTime: event.shareTime,    createTime: now  })  return { successtrue }}

九、高频问题排查

        整理日常开发中遇到最多的问题,对应原因和解决方案,遇到问题直接对照排查:

问题现象
根本原因
解决办法
分享图片空白 / 不显示
图片域名未配置、非 HTTPS 链接
微信公众平台添加合法域名,图片使用 HTTPS 地址,格式选用 PNG/JPG
朋友圈分享入口消失
未调用uni.showShareMenu
在页面onLoad中主动调用,配置menus参数
小程序码生成失败
scene 参数超长、页面路径未发布
scene 控制在 32 字符内,确保填写的 page 路径已正式发布
App 端分享小程序失败
未绑定微信开放平台
开放平台创建移动应用,关联对应小程序
H5 分享无响应
JSSDK 签名错误、域名未配置
后端校验签名,公众平台配置 JS 安全域名
Canvas 海报真机模糊
未处理像素比
导出时通过destWidth/destHeight放大 2-3 倍
分享参数丢失
参数过长被截断
精简参数、编码处理或后端短链接映射

十、总结

        UniApp 多端分享看着功能繁杂,其实梳理清楚规则后并不难。核心逻辑再精简一遍:

  1. 微信小程序分享:onShareAppMessage(好友)+onShareTimeline(朋友圈),必须先开启分享菜单;
  2. 朋友圈牢记两大限制:不可改路径、query 参数≤20 字符
  3. 海报裂变方案:云函数拿小程序码 + Canvas 绘图 + 相册权限校验;
  4. 多端适配:App 用uni.share,H5 依赖微信 JSSDK;
  5. 上线前必查:域名白名单、图片格式、参数长度三大关键点。