微信小程序特殊场景速查
1. 微信登录认证
Section titled “1. 微信登录认证”1.1 流程分析
Section titled “1.1 流程分析”微信小程序登录认证流程如下:
- 小程序端调用
wx.login()获取临时登录凭证 code - 将 code 发送到后端服务器
- 后端服务器使用 code、appid 和 appsecret 调用微信接口换取 openid 和 session_key
- 后端生成自定义登录态(如 token)返回给小程序
- 小程序存储 token,后续请求携带此 token 进行身份验证
1.2 代码实现
Section titled “1.2 代码实现”小程序端代码
Section titled “小程序端代码”// 小程序登录wx.login({ success: (res) => { if (res.code) { // 发送 code 到后端 wx.request({ url: 'https://your-domain.com/api/login', method: 'POST', data: { code: res.code }, success: (res) => { // 存储后端返回的 token wx.setStorageSync('token', res.data.token); } }); } }});
// 检查登录状态function checkLoginStatus() { const token = wx.getStorageSync('token'); if (!token) { // 未登录,执行登录流程 wx.login(); return false; } return true;}后端代码(Node.js示例)
Section titled “后端代码(Node.js示例)”// 使用 code 换取 openid 和 session_keyconst axios = require('axios');
async function login(req, res) { const { code } = req.body;
try { const response = await axios.get('https://api.weixin.qq.com/sns/jscode2session', { params: { appid: 'YOUR_APPID', secret: 'YOUR_APPSECRET', js_code: code, grant_type: 'authorization_code' } });
const { openid, session_key } = response.data;
// 生成自定义 token const token = generateToken(openid);
// 存储用户信息到数据库 await saveOrUpdateUser(openid, session_key);
res.json({ success: true, token: token }); } catch (error) { res.status(500).json({ success: false, message: '登录失败' }); }}2. 微信支付对接
Section titled “2. 微信支付对接”2.1 流程分析
Section titled “2.1 流程分析”微信小程序支付流程如下:
- 用户在小程序内选择商品下单
- 小程序向商户服务器发起创建订单请求
- 商户服务器调用微信支付统一下单API创建预支付订单
- 微信支付返回预支付交易会话标识 prepay_id
- 商户服务器根据 prepay_id 生成支付参数并返回给小程序
- 小程序调用
wx.requestPayment发起支付 - 用户确认支付,输入密码
- 微信支付返回支付结果
- 商户服务器接收微信支付结果通知
- 商户服务器验证支付结果并更新订单状态
2.2 代码实现
Section titled “2.2 代码实现”小程序端代码
Section titled “小程序端代码”// 发起支付function requestPayment(orderInfo) { wx.requestPayment({ timeStamp: orderInfo.timeStamp, nonceStr: orderInfo.nonceStr, package: orderInfo.package, signType: orderInfo.signType, paySign: orderInfo.paySign, success: (res) => { // 支付成功 wx.showToast({ title: '支付成功', icon: 'success' });
// 跳转到支付结果页面 wx.navigateTo({ url: '/pages/payment-result/payment-result?orderId=' + orderInfo.orderId }); }, fail: (res) => { // 支付失败 wx.showToast({ title: '支付取消', icon: 'none' }); } });}
// 创建订单并支付function createOrderAndPay(productInfo) { wx.request({ url: 'https://your-domain.com/api/create-order', method: 'POST', data: productInfo, header: { 'Authorization': 'Bearer ' + wx.getStorageSync('token') }, success: (res) => { if (res.data.success) { // 调起支付 requestPayment(res.data.paymentParams); } } });}后端代码(Node.js示例)
Section titled “后端代码(Node.js示例)”// 创建订单const axios = require('axios');const crypto = require('crypto');
async function createOrder(req, res) { const { productId, amount } = req.body; const openid = req.user.openid; // 从中间件获取用户信息
try { // 生成商户订单号 const outTradeNo = generateOrderNo();
// 调用微信支付统一下单API const response = await axios.post('https://api.mch.weixin.qq.com/v3/pay/transactions/miniprogram', { appid: 'YOUR_APPID', mchid: 'YOUR_MCHID', description: '商品描述', out_trade_no: outTradeNo, notify_url: 'https://your-domain.com/api/payment-notify', amount: { total: amount, // 单位:分 currency: 'CNY' }, payer: { openid: openid } }, { headers: { 'Authorization': 'WECHATPAY2-SHA256-RSA2048 ' + generateSignature(), 'Content-Type': 'application/json' } });
const { prepay_id } = response.data;
// 生成小程序支付参数 const timeStamp = Math.floor(Date.now() / 1000).toString(); const nonceStr = generateNonceStr(); const pkg = `prepay_id=${prepay_id}`;
// 生成签名 const paySign = generatePaySign({ appId: 'YOUR_APPID', timeStamp, nonceStr, package: pkg, signType: 'RSA' });
// 保存订单信息到数据库 await saveOrder({ outTradeNo, prepayId: prepay_id, openid, amount, status: 'UNPAID' });
res.json({ success: true, paymentParams: { timeStamp, nonceStr, package: pkg, signType: 'RSA', paySign, orderId: outTradeNo } }); } catch (error) { res.status(500).json({ success: false, message: '创建订单失败' }); }}
// 支付结果通知处理async function paymentNotify(req, res) { try { // 验证签名 const signature = req.headers['wechatpay-signature']; const timestamp = req.headers['wechatpay-timestamp']; const nonce = req.headers['wechatpay-nonce']; const body = JSON.stringify(req.body);
if (!verifySignature(signature, timestamp, nonce, body)) { return res.status(400).json({ code: 'FAIL', message: '签名验证失败' }); }
// 解密通知数据 const notifyData = decryptNotifyData(req.body.resource);
// 更新订单状态 await updateOrderStatus(notifyData.out_trade_no, 'PAID');
// 返回成功响应 res.json({ code: 'SUCCESS', message: '成功' }); } catch (error) { res.status(500).json({ code: 'FAIL', message: '处理通知失败' }); }}3. 页面配置
Section titled “3. 页面配置”微信小程序的页面配置可以在app.json全局配置和每个页面的单独.json文件中进行。合理的页面配置可以提升用户体验和小程序性能。
3.1 启用自定义导航栏
Section titled “3.1 启用自定义导航栏”要启用自定义导航栏,需要在页面配置中设置 navigationStyle: custom。
// 页面单独配置 - pages/custom-nav/custom-nav.json{ "navigationStyle": "custom"}也可以在app.json中全局配置:
// app.json全局配置{ "window": { "navigationStyle": "custom" }}3.2 页面基本配置
Section titled “3.2 页面基本配置”{ "pages": [ "pages/index/index", "pages/detail/detail", "pages/user/user" ], "window": { "backgroundTextStyle": "light", "navigationBarBackgroundColor": "#fff", "navigationBarTitleText": "小程序标题", "navigationBarTextStyle": "black" }, "tabBar": { "color": "#7A7E83", "selectedColor": "#3cc51f", "borderStyle": "black", "backgroundColor": "#ffffff", "list": [ { "pagePath": "pages/index/index", "iconPath": "images/icon_home.png", "selectedIconPath": "images/icon_home_active.png", "text": "首页" }, { "pagePath": "pages/user/user", "iconPath": "images/icon_user.png", "selectedIconPath": "images/icon_user_active.png", "text": "我的" } ] }}4. 其他常用场景
Section titled “4. 其他常用场景”4.1 获取用户信息
Section titled “4.1 获取用户信息”// 获取用户信息wx.getUserProfile({ desc: '用于完善会员资料', success: (res) => { const { userInfo } = res; // 发送用户信息到后端 wx.request({ url: 'https://your-domain.com/api/update-user-info', method: 'POST', data: userInfo, header: { 'Authorization': 'Bearer ' + wx.getStorageSync('token') } }); }});4.2 分享功能
Section titled “4.2 分享功能”// 页面分享Page({ onShareAppMessage() { return { title: '分享标题', path: '/pages/index/index', imageUrl: '/images/share-image.jpg' }; },
// 分享到朋友圈 onShareTimeline() { return { title: '分享标题', query: 'id=123', imageUrl: '/images/share-image.jpg' }; }});4.3 扫码功能
Section titled “4.3 扫码功能”// 调起扫码wx.scanCode({ success: (res) => { console.log(res.result); // 处理扫码结果 }});4.4 自定义导航栏
Section titled “4.4 自定义导航栏”4.4.1 流程分析
Section titled “4.4.1 流程分析”微信小程序自定义导航栏流程如下:
- 在页面配置中设置
navigationStyle: custom启用自定义导航栏 - 获取系统信息(状态栏高度)和胶囊按钮信息
- 计算导航栏高度 = 状态栏高度 + 胶囊按钮高度 + 上下间距
- 创建自定义导航栏组件,适配不同设备
4.4.2 代码实现
Section titled “4.4.2 代码实现”// 获取导航栏信息function getNavBarInfo() { const systemInfo = wx.getSystemInfoSync(); const capsuleInfo = wx.getMenuButtonBoundingClientRect();
// 导航栏高度 = 状态栏高度 + 胶囊按钮高度 + 上下间距 const navBarHeight = systemInfo.statusBarHeight + capsuleInfo.height + (capsuleInfo.top - systemInfo.statusBarHeight) * 2;
return { statusBarHeight: systemInfo.statusBarHeight, navBarHeight, capsuleInfo };}
// 页面中使用Page({ data: { navBarHeight: 0, statusBarHeight: 0 },
onLoad() { const navInfo = getNavBarInfo(); this.setData({ navBarHeight: navInfo.navBarHeight, statusBarHeight: navInfo.statusBarHeight }); }});<!-- 自定义导航栏 WXML --><view class="nav-bar" style="height: {{navBarHeight}}px;"> <view class="nav-content" style="top: {{statusBarHeight}}px;"> <view class="nav-back" bindtap="goBack"> <text>返回</text> </view> <view class="nav-title"> {{title}} </view> </view></view>
<!-- 页面内容 --><view class="page-content" style="padding-top: {{navBarHeight}}px;"> <!-- 页面其他内容 --></view>/* 自定义导航栏样式 */.nav-bar { position: fixed; top: 0; left: 0; width: 100%; background-color: #fff; z-index: 999;}
.nav-content { position: relative; width: 100%; height: 44px; display: flex; align-items: center; padding: 0 15px; box-sizing: border-box;}
.nav-back { width: 60px; font-size: 14px;}
.nav-title { flex: 1; text-align: center; font-size: 16px; font-weight: bold; margin-right: 60px;}
.page-content { width: 100%; min-height: 100vh; box-sizing: border-box;}4.5 文件下载
Section titled “4.5 文件下载”4.5.1 流程分析
Section titled “4.5.1 流程分析”微信小程序文件下载流程如下:
- 小程序端调用
wx.downloadFile下载文件到本地临时目录 - 下载成功后获取文件本地路径
- 根据文件类型选择打开方式或保存到用户手机
- 对于文档类文件,可调用
wx.openDocument打开 - 对于图片类文件,可保存到相册或直接显示
4.5.2 代码实现
Section titled “4.5.2 代码实现”// 下载文件function downloadFile(fileUrl, fileName) { wx.showLoading({ title: '下载中...' });
wx.downloadFile({ url: fileUrl, success: (res) => { if (res.statusCode === 200) { const filePath = res.tempFilePath;
// 根据文件类型处理 if (isDocumentFile(fileName)) { // 打开文档 openDocument(filePath, fileName); } else if (isImageFile(fileName)) { // 保存图片到相册 saveImageToPhotosAlbum(filePath); } } }, fail: (err) => { wx.showToast({ title: '下载失败', icon: 'none' }); console.error('下载失败:', err); }, complete: () => { wx.hideLoading(); } });}
// 打开文档function openDocument(filePath, fileName) { wx.openDocument({ filePath: filePath, fileType: getFileType(fileName), success: (res) => { console.log('打开文档成功'); }, fail: (err) => { wx.showToast({ title: '打开文档失败', icon: 'none' }); console.error('打开文档失败:', err); } });}
// 保存图片到相册function saveImageToPhotosAlbum(filePath) { // 先请求授权 wx.getSetting({ success: (res) => { if (!res.authSetting['scope.writePhotosAlbum']) { wx.authorize({ scope: 'scope.writePhotosAlbum', success: () => { // 授权成功,保存图片 saveImage(filePath); }, fail: () => { // 授权失败,引导用户手动开启 wx.showModal({ title: '提示', content: '需要您授权保存图片到相册', success: (res) => { if (res.confirm) { wx.openSetting(); } } }); } }); } else { // 已授权,直接保存 saveImage(filePath); } } });}
// 实际保存图片function saveImage(filePath) { wx.saveImageToPhotosAlbum({ filePath: filePath, success: () => { wx.showToast({ title: '保存成功', icon: 'success' }); }, fail: (err) => { wx.showToast({ title: '保存失败', icon: 'none' }); console.error('保存图片失败:', err); } });}
// 工具函数:判断是否为文档文件function isDocumentFile(fileName) { const docTypes = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt']; const fileType = getFileType(fileName); return docTypes.includes(fileType);}
// 工具函数:判断是否为图片文件function isImageFile(fileName) { const imageTypes = ['jpg', 'jpeg', 'png', 'gif', 'webp']; const fileType = getFileType(fileName); return imageTypes.includes(fileType);}
// 工具函数:获取文件类型function getFileType(fileName) { return fileName.split('.').pop().toLowerCase();}
// 下载并预览PDF文件function downloadAndPreviewPDF(pdfUrl, fileName) { wx.downloadFile({ url: pdfUrl, success: (res) => { if (res.statusCode === 200) { wx.openDocument({ filePath: res.tempFilePath, fileType: 'pdf', success: function (res) { console.log('打开PDF成功'); }, fail: function (err) { console.error('打开PDF失败:', err); } }); } }, fail: (err) => { wx.showToast({ title: '下载失败', icon: 'none' }); } });}4. 最佳实践
Section titled “4. 最佳实践”-
安全性:
- 敏感操作(如支付)应在后端完成
- 使用 HTTPS 协议传输数据
- 对用户输入进行验证和过滤
-
性能优化:
- 合理使用缓存机制
- 避免频繁调用微信 API
- 使用分包加载减少主包体积
-
用户体验:
- 提供加载状态提示
- 处理网络异常情况
- 优化页面加载速度
5. 常见问题解决
Section titled “5. 常见问题解决”-
登录失效处理:
- 检测到登录态失效时,自动重新登录
- 使用拦截器统一处理请求错误
-
支付异常处理:
- 支付完成后主动查询订单状态
- 实现支付结果轮询机制
-
接口调用频率限制:
- 合理控制接口调用频率
- 使用缓存减少不必要的请求
-
文件下载问题:
- 下载大文件时显示进度条
- 处理网络中断导致的下载失败
- 文件类型不支持时的友好提示
- 临时文件清理机制
本文档基于 2024 年最新微信小程序 API 编写,适用于面试前快速复习。