本教程将带领大家从零搭建Uni-app项目,适配H5端并集成微信公众号核心功能(如网页授权、JS-SDK调用、分享等),最终完成项目打包部署。全程配套实操步骤与代码示例,适合前端新手或需要快速落地公众号H5项目的开发者。

一、前置知识与环境准备

1.1 必备知识

  • 基础HTML/CSS/JavaScript语法

  • Vue.js基础(Uni-app基于Vue语法,Vue3为主)

  • 微信公众号基本概念(订阅号/服务号、AppID、网页授权等)

1.2 环境搭建

1.2.1 安装开发工具

Uni-app推荐使用HBuilderX开发,内置Uni-app编译环境,无需额外配置:

  1. 下载HBuilderX:https://www.dcloud.io/hbuilderx.html(选择「正式版」,根据系统选择Windows/Mac)

  2. 安装完成后,打开HBuilderX,进入「工具」-「插件安装」,搜索「Uni-app」插件并安装(默认已安装)

1.2.2 安装Node.js(可选)

若需要使用npm安装依赖(如微信JS-SDK),需安装Node.js:

  1. 下载Node.js:https://nodejs.org/zh-cn/(选择LTS长期支持版)

  2. 安装完成后,打开终端,输入node -vnpm -v ,若显示版本号则安装成功

1.2.3 微信公众号准备

需提前注册微信公众号(推荐服务号,订阅号部分接口权限受限),并完成基础配置:

  1. 注册/登录公众号:https://mp.weixin.qq.com/

  2. 获取AppID:登录后进入「设置与开发」-「基本配置」,记录「AppID(开发ID)」(后续项目配置需用到)

  3. 配置开发者工具:进入「设置与开发」-「开发者工具」-「web开发者工具」,绑定开发者微信号(用于本地调试)

二、创建Uni-app+H5项目

2.1 初始化Uni-app项目

  1. 打开HBuilderX,点击「文件」-「新建」-「项目」

  2. 选择「Uni-app」模板,输入项目名称(如「uniapp-wechat-h5」),选择「Vue3」,勾选「TypeScript」(可选,推荐),点击「创建」

  3. 项目目录说明(核心目录):

    • pages:存放页面文件(核心开发目录)

    • static:存放静态资源(图片、字体等)

    • main.ts:项目入口文件

    • pages.json:页面路由配置(Uni-app核心配置文件)

    • manifest.json:项目配置(如AppID、H5配置等)

2.2 配置H5端基础信息

打开项目根目录的「manifest.json」,进行H5端配置:

  1. 点击「H5配置」选项卡,填写「页面标题」(如「Uni-app公众号Demo」)

  2. 「运行基础路径」:本地开发时留空,部署时填写服务器访问路径(如「/uniapp-wechat/」)

  3. 「端口号」:默认8080,可自定义(避免端口占用)

  4. 「跨域设置」:点击「添加」,填写后端接口域名(如「https://api.example.com」),允许跨域请求

2.3 本地运行H5项目

  1. 右键项目根目录,选择「运行」-「运行到浏览器」-「选择浏览器(Chrome/Firefox等)」

  2. 等待编译完成,浏览器会自动打开项目(默认地址:http://localhost:8080),看到Uni-app默认首页则运行成功

三、公众号核心配置(网页授权+JS-SDK)

公众号H5开发的核心是集成微信网页授权(获取用户信息)和JS-SDK(调用微信原生功能,如分享、定位等),需先完成公众号后台配置与项目依赖引入。

3.1 公众号后台配置

3.1.1 配置网页授权域名

网页授权需指定域名(本地开发可使用内网穿透工具,如花生壳、natapp):

  1. 登录公众号后台,进入「设置与开发」-「公众号设置」-「功能设置」

  2. 找到「网页授权域名」,点击「设置」,输入需要授权的域名(如「example.com」,注意:无需http/https,且需备案)

  3. 下载校验文件,放置在服务器根目录(本地开发可跳过,内网穿透工具会自动处理),点击「保存」完成配置

3.1.2 配置JS接口安全域名

调用JS-SDK需配置安全域名:

  1. 进入「设置与开发」-「公众号设置」-「功能设置」,找到「JS接口安全域名」

  2. 点击「设置」,输入与网页授权一致的域名,下载校验文件并放置在服务器根目录,点击「保存」

3.2 项目集成微信JS-SDK

使用npm安装微信JS-SDK依赖(uni-app也可使用官方插件,此处推荐npm方式):


# 右键项目根目录,选择「在终端中打开」,输入以下命令
npm install weixin-js-sdk --save
    

3.3 封装微信授权与JS-SDK初始化工具

在项目根目录创建「utils/wechat.ts」文件,封装网页授权和JS-SDK初始化逻辑:


import wx from 'weixin-js-sdk';
import { getWechatConfig } from '@/api/wechat'; // 后续创建的后端接口请求

// 公众号AppID(从manifest.json或环境变量中获取)
const APPID = '你的公众号AppID';

/**
 * 微信网页授权(获取code,用于后端换取openid)
 * @param redirectUri 授权成功后跳转的页面路径(需编码)
 */
export const wechatAuth = (redirectUri: string = window.location.href) => {
  const encodedRedirectUri = encodeURIComponent(redirectUri);
  // 构造授权链接
  const authUrl = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${APPID}&redirect_uri=${encodedRedirectUri}&response_type=code&scope=snsapi_userinfo&state=123#wechat_redirect`;
  // 跳转到授权页面
  window.location.href = authUrl;
};

/**
 * JS-SDK初始化
 * @param url 当前页面URL(需与公众号安全域名一致)
 */
export const initWechatSdk = async (url: string = window.location.href) => {
  try {
    // 调用后端接口获取JS-SDK配置参数(timestamp、noncestr、signature等)
    const res = await getWechatConfig({ url });
    const { appId, timestamp, nonceStr, signature } = res.data;
    
    // 初始化JS-SDK
    wx.config({
      debug: false, // 开发阶段可设为true,查看调试信息
      appId,
      timestamp,
      nonceStr,
      signature,
      jsApiList: [
        'updateAppMessageShareData', // 自定义分享给朋友
        'updateTimelineShareData', // 自定义分享到朋友圈
        'getLocation', // 获取地理位置
        'scanQRCode', // 扫码
        'chooseImage', // 拍照/选图
        'uploadImage', // 上传图片到微信服务器
        'chooseWXPay', // 微信支付
        'requestSubscribeMessage' // 订阅消息授权
      ] // 需要调用的JS接口列表
    });
    
    // 初始化成功回调
    wx.ready(() => {
      console.log('JS-SDK初始化成功');
    });
    
    // 初始化失败回调
    wx.error((err: any) => {
      console.error('JS-SDK初始化失败:', err);
    });
  } catch (error) {
    console.error('获取JS-SDK配置失败:', error);
  }
};

/**
 * 自定义分享配置
 * @param title 分享标题
 * @param desc 分享描述
 * @param link 分享链接
 * @param imgUrl 分享图标
 */
export const setWechatShare = (title: string, desc: string, link: string, imgUrl: string) => {
  wx.ready(() => {
    // 分享给朋友
    wx.updateAppMessageShareData({
      title,
      desc,
      link,
      imgUrl,
      success: () => {
        console.log('分享给朋友配置成功');
      }
    });
    
    // 分享到朋友圈
    wx.updateTimelineShareData({
      title,
      link,
      imgUrl,
      success: () => {
        console.log('分享到朋友圈配置成功');
      }
    });
  });
};

/**
 * 获取用户地理位置
 * @param type 坐标类型(wgs84:GPS坐标,gcj02:火星坐标)
 */
export const getWechatLocation = () => {
  return new Promise((resolve, reject) => {
    wx.ready(() => {
      wx.getLocation({
        type: 'gcj02',
        success: (res) => {
          const { latitude, longitude } = res; // 纬度、经度
          resolve({ latitude, longitude });
        },
        fail: (err) => {
          console.error('获取地理位置失败:', err);
          uni.showToast({ title: '获取位置失败,请允许定位权限', icon: 'none' });
          reject(err);
        }
      });
    });
  });
};

/**
 * 拍照/选图并上传到微信服务器
 * @param count 最多选择图片数量
 */
export const chooseAndUploadImage = (count: number = 1) => {
  return new Promise((resolve, reject) => {
    wx.ready(() => {
      // 1. 选择图片(拍照/从相册选)
      wx.chooseImage({
        count,
        sizeType: ['original', 'compressed'], // 原图/压缩图
        sourceType: ['album', 'camera'], // 相册/相机
        success: (chooseRes) => {
          const localIds = chooseRes.localIds; // 本地图片ID列表
          // 2. 上传图片到微信服务器
          wx.uploadImage({
            localId: localIds[0], // 上传第一张图片(可循环上传多张)
            isShowProgressTips: 1, // 显示进度提示
            success: (uploadRes) => {
              const serverId = uploadRes.serverId; // 微信服务器返回的图片ID(需传给后端下载)
              resolve({ localId: localIds[0], serverId });
            },
            fail: (err) => {
              console.error('图片上传失败:', err);
              reject(err);
            }
          });
        },
        fail: (err) => {
          console.error('选择图片失败:', err);
          reject(err);
        }
      });
    });
  });
};

/**
 * 检测微信版本是否满足要求
 * @param requiredVersion 要求的版本(如'7.0.0')
 * @returns 是否满足
 */
export const checkWechatVersion = (requiredVersion: string) => {
  const wechatVersion = navigator.userAgent.match(/MicroMessenger\/(\d+\.\d+\.\d+)/)?.[1] || '';
  if (!wechatVersion) return false;
  const versionArr = wechatVersion.split('.').map(Number);
  const requiredArr = requiredVersion.split('.').map(Number);
  for (let i = 0; i < versionArr.length; i++) {
    if (versionArr[i] > requiredArr[i]) return true;
    if (versionArr[i] < requiredArr[i]) return false;
  }
  return true;
};

3.4 创建后端接口请求(api/wechat.ts)

封装获取JS-SDK配置参数的接口请求(需后端配合实现签名生成逻辑):


import request from '@/utils/request';

/**
 * 获取微信JS-SDK配置参数
 * @param params { url: string } 当前页面URL
 */
export const getWechatConfig = (params: { url: string }) => {
  return request({
    url: '/api/wechat/config', // 后端接口地址
    method: 'get',
    params
  });
};

/**
 * 用code换取用户信息(openid、昵称等)
 * @param params { code: string } 网页授权获取的code
 */
export const getUserInfoByCode = (params: { code: string }) => {
  return request({
    url: '/api/wechat/userInfo',
    method: 'get',
    params
  });
};

/**
 * 创建订单并获取支付参数
 * @param data { orderId: string, totalFee: number } 订单ID、订单金额(分)
 */
export const createOrderAndGetPayParams = (data: { orderId: string, totalFee: number }) => {
  return request({
    url: '/api/wechat/pay/createOrder',
    method: 'post',
    data
  });
};

/**
 * 查询支付结果
 * @param params { orderId: string } 订单ID
 */
export const queryPayResult = (params: { orderId: string }) => {
  return request({
    url: '/api/wechat/pay/queryResult',
    method: 'get',
    params
  });
};

/**
 * 发送订阅消息
 * @param data { templateId: string, page: string, data: any } 模板ID、跳转页面、模板数据
 */
export const sendSubscribeMessage = (data: {
  templateId: string,
  page: string,
  data: Record<string, { value: string }>
}) => {
  return request({
    url: '/api/wechat/subscribe/send',
    method: 'post',
    data
  });
};

3.5 封装请求工具(utils/request.ts)

基于uni.request封装全局请求工具,处理请求拦截、响应拦截:


import { showToast } from 'uni-app';

// 创建请求实例
const request = (options: any) => {
  return new Promise((resolve, reject) => {
    uni.request({
      url: options.url,
      method: options.method || 'GET',
      data: options.data || {},
      params: options.params || {},
      header: {
        'Content-Type': 'application/json',
        ...options.header
      },
      success: (res) => {
        // 响应状态码判断
        if (res.statusCode === 200) {
          resolve(res.data);
        } else {
          showToast({
            title: res.data.message || '请求失败',
            icon: 'none'
          });
          reject(res.data);
        }
      },
      fail: (err) => {
        showToast({
          title: '网络错误',
          icon: 'none'
        });
        reject(err);
      }
    });
  });
};

export default request;
    

四、核心功能开发实战

本节将实现3个核心功能:首页(触发微信授权+分享配置)、用户中心(展示用户信息)、扫码功能页,配套完整代码示例。

4.1 配置页面路由(pages.json)

修改pages.json,添加首页、用户中心、扫码页的路由配置:


{
  "pages": [
    {
      "path": "pages/index/index",
      "style": {
        "navigationBarTitleText": "首页"
      }
    },
    {
      "path": "pages/user/user",
      "style": {
        "navigationBarTitleText": "用户中心"
      }
    },
    {
      "path": "pages/scan/scan",
      "style": {
        "navigationBarTitleText": "扫码功能"
      }
    },
    {
      "path": "pages/extend/extend",
      "style": {
        "navigationBarTitleText": "更多功能演示"
      }
    },
    {
      "path": "pages/pay/pay",
      "style": {
        "navigationBarTitleText": "确认支付"
      }
    },
    {
      "path": "pages/order/detail",
      "style": {
        "navigationBarTitleText": "订单详情"
      }
    }
  ],
  "globalStyle": {
    "navigationBarTextStyle": "black",
    "navigationBarTitleText": "Uni-app公众号Demo",
    "navigationBarBackgroundColor": "#F8F8F8",
    "backgroundColor": "#F8F8F8"
  }
}

4.2 首页开发(pages/index/index.vue)

实现功能:页面加载时触发微信授权,获取用户信息后存储到本地,配置微信分享,添加跳转按钮:


<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { wechatAuth, initWechatSdk, setWechatShare, checkWechatVersion } from '@/utils/wechat';
import { getUserInfoByCode } from '@/api/wechat';
import { useRouter } from 'uni-app';

const router = useRouter();
const userInfo = ref<any>(null);

onMounted(() => {
  // 检测微信版本
  if (!checkWechatVersion('7.0.0')) {
    uni.showToast({ title: '微信版本过低,请升级后使用', icon: 'none' });
  }
  // 1. 初始化JS-SDK
  initWechatSdk();
  
  // 2. 配置微信分享
  const shareConfig = {
    title: 'Uni-app公众号Demo',
    desc: '从零开始开发的Uni-app+H5公众号项目',
    link: window.location.href,
    imgUrl: 'https://example.com/logo.png' // 分享图标(需放在安全域名下)
  };
  setWechatShare(shareConfig.title, shareConfig.desc, shareConfig.link, shareConfig.imgUrl);
  
  // 3. 处理微信授权回调(获取code)
  handleAuthCallback();
});

/**
 * 处理微信授权回调,获取code并换取用户信息
 */
const handleAuthCallback = () => {
  // 从URL中解析code(微信授权成功后会将code拼接到redirectUri后)
  const urlParams = new URLSearchParams(window.location.search);
  const code = urlParams.get('code');
  const localUserInfo = uni.getStorageSync('userInfo');
  
  if (localUserInfo) {
    // 本地已存储用户信息,直接读取
    userInfo.value = localUserInfo;
  } else if (code) {
    // 有code,调用接口换取用户信息
    getUserInfoByCode({ code }).then(res => {
      userInfo.value = res.data;
      // 存储用户信息到本地
      uni.setStorageSync('userInfo', res.data);
    });
  } else {
    // 无code,触发微信授权
    wechatAuth();
  }
};

/**
 * 跳转用户中心
 */
const toUserPage = () => {
  router.push('/pages/user/user');
};

/**
 * 跳转扫码页
 */
const toScanPage = () => {
  router.push('/pages/scan/scan');
};

/**
 * 跳转更多功能演示页
 */
const toExtendPage = () => {
  router.push('/pages/extend/extend');
};

/**
 * 跳转支付测试页
 */
const toPayPage = () => {
  // 模拟订单参数
  router.push({
    path: '/pages/pay/pay',
    query: {
      orderId: 'TEST' + Date.now(),
      totalFee: 1 // 1分
    }
  });
};

</script>

4.3 用户中心开发(pages/user/user.vue)

实现功能:展示本地存储的用户信息,添加退出登录按钮:


<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useRouter } from 'uni-app';

const router = useRouter();
const userInfo = ref<any>(null);

onMounted(() => {
  // 读取本地存储的用户信息
  const localUserInfo = uni.getStorageSync('userInfo');
  if (localUserInfo) {
    userInfo.value = localUserInfo;
  } else {
    // 无用户信息,跳转首页触发授权
    router.push('/pages/index/index');
  }
});

/**
 * 退出登录:清除本地存储,跳转首页
 */
const logout = () => {
  uni.removeStorageSync('userInfo');
  router.push('/pages/index/index');
};

</script>

4.4 扫码功能开发(pages/scan/scan.vue)

实现功能:调用微信JS-SDK的扫码接口,展示扫码结果:


<script setup lang="ts">
import { ref } from 'vue';
import wx from 'weixin-js-sdk';
import { initWechatSdk } from '@/utils/wechat';

const scanResult = ref<string>('');

// 页面加载时初始化JS-SDK(确保扫码接口可用)
initWechatSdk();

/**
 * 调用微信扫码接口
 */
const scanQrCode = () => {
  wx.ready(() => {
    wx.scanQRCode({
      needResult: 1, // 1表示直接返回扫描结果,0表示跳转第三方处理
      scanType: ['qrCode', 'barCode'], // 可扫描的类型(二维码、条形码)
      success: (res: any) => {
        // res.resultStr 为扫码结果
        scanResult.value = res.resultStr;
      },
      fail: (err: any) => {
        uni.showToast({
          title: '扫码失败',
          icon: 'none'
        });
        console.error('扫码失败:', err);
      }
    });
  });
};

</script>

五、项目打包与部署

5.1 打包H5项目

  1. 右键项目根目录,选择「发行」-「网站-H5手机版」

  2. 在弹出的打包配置窗口中,填写「网站标题」「网站域名」(即公众号配置的安全域名),点击「发行」

  3. 等待打包完成,HBuilderX会自动打开打包后的文件夹(默认路径:unpackage/dist/build/h5)

5.2 部署到服务器

将打包后的h5文件夹内容上传到服务器的对应目录(需与公众号配置的安全域名一致),推荐使用FTP工具(如FileZilla)上传:

  1. 打开FTP工具,连接服务器(输入服务器IP、用户名、密码、端口)

  2. 将打包后的所有文件(html、css、js、static等)上传到服务器的网站根目录(如「/www/wwwroot/example.com/」)

  3. 上传完成后,通过域名访问(如「https://example.com」),能正常打开项目则部署成功

5.3 公众号菜单配置(可选)

登录公众号后台,进入「功能」-「自定义菜单」,添加菜单并关联部署后的H5页面链接,用户点击菜单即可直接进入对应页面。

六、常见问题解决

6.1 微信授权失败

  • 检查网页授权域名是否正确配置,且已放置校验文件

  • 授权链接的redirectUri需编码,且与授权域名一致

  • 确保公众号为服务号,订阅号无网页授权获取用户信息的权限

6.2 JS-SDK初始化失败

  • 检查JS接口安全域名配置是否正确

  • 后端生成签名时使用的url需与当前页面url一致(包括query参数,不包括#后的锚点)

  • 确认appId、timestamp、nonceStr、signature参数正确无误

6.3 本地开发跨域问题

  • 在manifest.json的H5配置中添加跨域域名

  • 使用后端代理解决跨域(如Nginx反向代理、Node.js中间件)

八、扩展功能与优化

8.1 本地调试公众号功能详细注意事项

本地开发公众号H5时,因微信接口需域名验证,直接用localhost无法正常调用授权、JS-SDK等功能,需重点关注以下调试要点:

8.1.1 内网穿透工具使用(必选)

通过内网穿透将本地服务映射为公网域名,推荐使用natapp(操作简单,适合新手):

  1. 注册登录natapp:https://natapp.cn/,购买免费/付费隧道(免费隧道域名随机,付费可固定)

  2. 配置隧道:隧道管理→配置,填写本地端口(如HBuilderX运行的8080),其他默认

  3. 下载natapp客户端,运行命令启动隧道(Windows示例):natapp -authtoken=你的隧道authtoken

  4. 启动成功后,获取公网域名(如xxxx.natappfree.cc),将该域名配置到公众号网页授权域名、JS接口安全域名(需提前在natapp后台完成域名备案关联)

8.1.2 微信开发者工具调试
  1. 下载微信开发者工具:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Web_Developer_Tools.html

  2. 打开工具,选择「公众号网页项目」,输入内网穿透的公网域名(如https://xxxx.natappfree.cc),勾选「调试模式」和「不校验域名安全性」(仅本地调试可用)

  3. 调试授权功能时,需登录与公众号绑定的开发者微信号,工具会模拟微信环境触发授权流程

8.1.3 调试避坑要点
  • 内网穿透隧道重启后域名可能变化,需重新配置公众号安全域名(免费隧道特性),建议开发期间保留隧道运行

  • 授权回调地址需与内网穿透域名一致,且已编码(参考wechat.ts中wechatAuth函数的编码逻辑)

  • 调试JS-SDK时,将wx.config的debug设为true,通过开发者工具控制台查看配置错误信息(如签名错误、域名未配置)

  • 本地跨域调试:优先在manifest.json的H5配置中添加后端接口域名,若仍有问题,可使用HBuilderX的「代理」功能(manifest.json→H5配置→代理设置)

8.2 更多JS-SDK高频功能实现

扩展原有JS-SDK能力,新增「获取地理位置」「拍照/选图上传」功能,需先更新JS接口列表并封装对应函数。

8.2.1 更新wechat.ts配置与封装函数

// 1. 更新jsApiList,添加新接口
jsApiList: [
  'updateAppMessageShareData',
  'updateTimelineShareData',
  'getLocation',
  'scanQRCode',
  'chooseImage', // 拍照/选图
  'uploadImage'  // 上传图片到微信服务器
]

// 2. 新增获取地理位置函数
/**
 * 获取用户地理位置
 * @param type 坐标类型(wgs84:GPS坐标,gcj02:火星坐标)
 */
export const getWechatLocation = () => {
  return new Promise((resolve, reject) => {
    wx.ready(() => {
      wx.getLocation({
        type: 'gcj02',
        success: (res) => {
          const { latitude, longitude } = res; // 纬度、经度
          resolve({ latitude, longitude });
        },
        fail: (err) => {
          console.error('获取地理位置失败:', err);
          uni.showToast({ title: '获取位置失败,请允许定位权限', icon: 'none' });
          reject(err);
        }
      });
    });
  });
};

// 3. 新增拍照/选图上传函数
/**
 * 拍照/选图并上传到微信服务器
 * @param count 最多选择图片数量
 */
export const chooseAndUploadImage = (count: number = 1) => {
  return new Promise((resolve, reject) => {
    wx.ready(() => {
      // 1. 选择图片(拍照/从相册选)
      wx.chooseImage({
        count,
        sizeType: ['original', 'compressed'], // 原图/压缩图
        sourceType: ['album', 'camera'], // 相册/相机
        success: (chooseRes) => {
          const localIds = chooseRes.localIds; // 本地图片ID列表
          // 2. 上传图片到微信服务器
          wx.uploadImage({
            localId: localIds[0], // 上传第一张图片(可循环上传多张)
            isShowProgressTips: 1, // 显示进度提示
            success: (uploadRes) => {
              const serverId = uploadRes.serverId; // 微信服务器返回的图片ID(需传给后端下载)
              resolve({ localId: localIds[0], serverId });
            },
            fail: (err) => {
              console.error('图片上传失败:', err);
              reject(err);
            }
          });
        },
        fail: (err) => {
          console.error('选择图片失败:', err);
          reject(err);
        }
      });
    });
  });
};
8.2.2 页面调用示例(新增pages/extend/extend.vue)

先在pages.json添加路由:


{
  "path": "pages/extend/extend",
  "style": {
    "navigationBarTitleText": "更多功能演示"
  }
}

页面代码:


<script setup lang="ts">
import { ref } from 'vue';
import { initWechatSdk, getWechatLocation, chooseAndUploadImage } from '@/utils/wechat';

const locationInfo = ref<any>(null);
const imageUrl = ref<string>('');

// 初始化JS-SDK
initWechatSdk();

// 获取地理位置
const getLocation = async () => {
  try {
    const res = await getWechatLocation();
    locationInfo.value = res;
  } catch (err) {
    console.error(err);
  }
};

// 拍照/选图上传
const chooseImage = async () => {
  try {
    const res = await chooseAndUploadImage();
    imageUrl.value = res.localId; // 显示本地预览图
    // 若需上传到自己的服务器,将res.serverId传给后端,由后端从微信服务器下载
    console.log('微信服务器图片ID:', res.serverId);
  } catch (err) {
    console.error(err);
  }
};

</script>

8.3 微信支付集成

公众号微信支付需后端配合完成签名生成、订单创建等逻辑,前端核心负责获取支付参数、调起支付、处理支付结果。

8.3.1 公众号支付前置配置
  1. 登录公众号后台,进入「微信支付」-「商户号关联」,绑定已开通的微信支付商户号

  2. 配置「支付授权目录」:进入商户号后台→「产品中心」→「开发配置」,添加支付授权目录(需与项目部署域名一致,如https://example.com/pages/pay/

  3. 确保项目已部署到公网(本地调试需用内网穿透域名,且已添加到支付授权目录)

8.3.2 前端支付功能实现
  1. 新增支付相关接口(api/wechat.ts):

/**
 * 创建订单并获取支付参数
 * @param data { orderId: string, totalFee: number } 订单ID、订单金额(分)
 */
export const createOrderAndGetPayParams = (data: { orderId: string, totalFee: number }) => {
  return request({
    url: '/api/wechat/pay/createOrder',
    method: 'post',
    data
  });
};

/**
 * 查询支付结果
 * @param params { orderId: string } 订单ID
 */
export const queryPayResult = (params: { orderId: string }) => {
  return request({
    url: '/api/wechat/pay/queryResult',
    method: 'get',
    params
  });
};
  1. 新增支付页面(pages/pay/pay.vue):

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useRoute, useRouter } from 'uni-app';
import { createOrderAndGetPayParams, queryPayResult } from '@/api/wechat';
import wx from 'weixin-js-sdk';
import { initWechatSdk } from '@/utils/wechat';

const route = useRoute();
const router = useRouter();
const orderId = ref<string>(route.query.orderId as string); // 从路由获取订单ID
const totalFee = ref<number>(Number(route.query.totalFee) || 0); // 订单金额(分)

// 初始化JS-SDK(支付需依赖微信环境)
onMounted(() => {
  initWechatSdk();
});

/**
 * 发起微信支付
 */
const doWechatPay = async () => {
  try {
    // 1. 获取支付参数(后端返回appId、timeStamp、nonceStr、package、signType、paySign)
    const res = await createOrderAndGetPayParams({
      orderId: orderId.value,
      totalFee: totalFee.value
    });
    const payParams = res.data;

    // 2. 调起微信支付
    wx.ready(() => {
      wx.chooseWXPay({
        appId: payParams.appId,
        timestamp: payParams.timeStamp,
        nonceStr: payParams.nonceStr,
        package: payParams.package,
        signType: payParams.signType,
        paySign: payParams.paySign,
        // 支付成功回调
        success: async (payRes) => {
          if (payRes.errMsg === 'chooseWXPay:ok') {
            // 3. 验证支付结果(前端回调不可信,需调用后端接口查询)
            const resultRes = await queryPayResult({ orderId: orderId.value });
            if (resultRes.data.success) {
              uni.showToast({ title: '支付成功' });
              // 跳转订单详情页
              router.push(`/pages/order/detail?orderId=${orderId.value}`);
            } else {
              uni.showToast({ title: '支付结果验证失败,请稍后查询', icon: 'none' });
            }
          }
        },
        // 支付失败回调
        fail: (err) => {
          console.error('支付失败:', err);
          uni.showToast({ title: '支付失败:' + err.errMsg, icon: 'none' });
        }
      });
    });
  } catch (err) {
    console.error('发起支付失败:', err);
    uni.showToast({ title: '发起支付失败', icon: 'none' });
  }
};

</script>

8.4 模板消息/订阅消息配置与调用

模板消息仅服务号可使用,订阅消息支持所有公众号类型(更推荐使用),用于向用户推送服务通知(需用户主动授权)。

8.4.1 订阅消息前置配置
  1. 登录公众号后台,进入「订阅消息」-「我的模板」,搜索并选用合适的模板(如“订单支付成功通知”),记录模板ID

  2. 模板选用后,记录模板的字段名称(如“thing1”对应订单名称,“amount2”对应支付金额)

8.4.2 前端实现订阅消息授权与发送
  1. 新增订阅消息相关接口(api/wechat.ts):

/**
 * 发送订阅消息
 * @param data { templateId: string, page: string, data: any } 模板ID、跳转页面、模板数据
 */
export const sendSubscribeMessage = (data: {
  templateId: string,
  page: string,
  data: Record<string, { value: string }>
}) => {
  return request({
    url: '/api/wechat/subscribe/send',
    method: 'post',
    data
  });
};
  1. 页面实现授权与发送(以支付成功后发送为例):

// 在pay.vue的支付成功回调中添加订阅消息授权与发送逻辑
const requestSubscribeMessage = async () => {
  return new Promise((resolve, reject) => {
    wx.ready(() => {
      // 1. 请求用户订阅授权(需用户点击允许)
      wx.requestSubscribeMessage({
        tmplIds: ['你的订阅消息模板ID'], // 模板ID列表
        success: (res) => {
          // 若用户允许订阅,发送消息
          if (res['你的订阅消息模板ID'] === 'accept') {
            resolve(true);
          } else {
            uni.showToast({ title: '未授权订阅消息,无法接收通知', icon: 'none' });
            resolve(false);
          }
        },
        fail: (err) => {
          console.error('订阅授权失败:', err);
          reject(err);
        }
      });
    });
  });
};

// 在支付成功的resultRes之后调用
const hasSubscribe = await requestSubscribeMessage();
if (hasSubscribe) {
  await sendSubscribeMessage({
    templateId: '你的订阅消息模板ID',
    page: `/pages/order/detail?orderId=${orderId.value}`, // 点击消息跳转的页面
    data: {
      thing1: { value: '测试订单' }, // 对应模板的字段名称
      amount2: { value: (totalFee.value / 100) + '元' },
      time3: { value: new Date().toLocaleString() }
    }
  });
  uni.showToast({ title: '订阅消息发送成功' });
}

8.5 项目优化(H5性能+微信适配)

8.5.1 H5性能优化
  • 代码分割与懒加载:Uni-app默认支持路由懒加载,无需额外配置;对于大型组件(如编辑器、图表),使用动态导入(import())减少首屏加载体积

  • 静态资源优化
    图片压缩:使用tinypng等工具压缩图片,优先使用webp格式(微信内置浏览器支持)

  • 静态资源CDN:将图片、字体等静态资源部署到CDN,减少服务器压力,提升加载速度

减少回流重绘:避免频繁操作DOM,使用flex布局替代float;图片提前设置宽高,避免加载后布局偏移

接口优化:合并重复接口请求,添加接口缓存(如用户信息);使用防抖节流处理高频交互(如搜索输入)

8.5.2 微信版本适配与兼容性处理
  • 微信版本检测:部分JS-SDK接口需较高微信版本支持,可通过以下代码检测版本:

/**
 * 检测微信版本是否满足要求
 * @param requiredVersion 要求的版本(如'7.0.0')
 * @returns 是否满足
 */
export const checkWechatVersion = (requiredVersion: string) => {
  const wechatVersion = navigator.userAgent.match(/MicroMessenger\/(\d+\.\d+\.\d+)/)?.[1] || '';
  if (!wechatVersion) return false;
  const versionArr = wechatVersion.split('.').map(Number);
  const requiredArr = requiredVersion.split('.').map(Number);
  for (let i = 0; i < versionArr.length; i++) {
    if (versionArr[i] > requiredArr[i]) return true;
    if (versionArr[i] < requiredArr[i]) return false;
  }
  return true;
};

// 使用示例:检测是否支持wx.updateAppMessageShareData
if (!checkWechatVersion('7.0.0')) {
  uni.showToast({ title: '微信版本过低,请升级后使用', icon: 'none' });
}
  • 适配暗黑模式:微信支持暗黑模式,可通过媒体查询适配:

@media (prefers-color-scheme: dark) {
  .container {
    background-color: #1a1a1a;
    color: #fff;
  }
  .btn {
    background-color: #333;
  }
}
  • 异常兼容:对未授权、接口调用失败等场景添加友好提示;使用try-catch包裹微信接口调用,避免程序崩溃

七、总结

本教程从零完成了Uni-app+H5公众号项目的搭建,核心流程为:环境准备→项目创建→公众号配置(网页授权+JS-SDK)→核心功能开发→打包部署。重点掌握微信网页授权的code获取与用户信息换取、JS-SDK的初始化与接口调用,即可满足大部分公众号H5项目的开发需求。

本教程已完整覆盖Uni-app+H5公众号开发的核心流程与扩展功能,包括基础搭建、公众号配置、核心功能开发、打包部署,以及微信支付、订阅消息、更多JS-SDK功能、本地调试技巧和项目优化等进阶内容。开发者可根据实际项目需求,基于本教程的代码示例进行扩展迭代。如需进一步深入学习,可参考微信公众号官方文档(https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html)和Uni-app官方文档(https://uniapp.dcloud.net.cn/)。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐