跳转到内容

微信小程序特殊场景速查

微信小程序登录认证流程如下:

  1. 小程序端调用 wx.login() 获取临时登录凭证 code
  2. 将 code 发送到后端服务器
  3. 后端服务器使用 code、appid 和 appsecret 调用微信接口换取 openid 和 session_key
  4. 后端生成自定义登录态(如 token)返回给小程序
  5. 小程序存储 token,后续请求携带此 token 进行身份验证
// 小程序登录
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;
}
// 使用 code 换取 openid 和 session_key
const 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: '登录失败'
});
}
}

微信小程序支付流程如下:

  1. 用户在小程序内选择商品下单
  2. 小程序向商户服务器发起创建订单请求
  3. 商户服务器调用微信支付统一下单API创建预支付订单
  4. 微信支付返回预支付交易会话标识 prepay_id
  5. 商户服务器根据 prepay_id 生成支付参数并返回给小程序
  6. 小程序调用 wx.requestPayment 发起支付
  7. 用户确认支付,输入密码
  8. 微信支付返回支付结果
  9. 商户服务器接收微信支付结果通知
  10. 商户服务器验证支付结果并更新订单状态
// 发起支付
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);
}
}
});
}
// 创建订单
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: '处理通知失败' });
}
}

微信小程序的页面配置可以在app.json全局配置和每个页面的单独.json文件中进行。合理的页面配置可以提升用户体验和小程序性能。

要启用自定义导航栏,需要在页面配置中设置 navigationStyle: custom

// 页面单独配置 - pages/custom-nav/custom-nav.json
{
"navigationStyle": "custom"
}

也可以在app.json中全局配置:

// app.json全局配置
{
"window": {
"navigationStyle": "custom"
}
}
{
"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": "我的"
}
]
}
}
// 获取用户信息
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')
}
});
}
});
// 页面分享
Page({
onShareAppMessage() {
return {
title: '分享标题',
path: '/pages/index/index',
imageUrl: '/images/share-image.jpg'
};
},
// 分享到朋友圈
onShareTimeline() {
return {
title: '分享标题',
query: 'id=123',
imageUrl: '/images/share-image.jpg'
};
}
});
// 调起扫码
wx.scanCode({
success: (res) => {
console.log(res.result);
// 处理扫码结果
}
});

微信小程序自定义导航栏流程如下:

  1. 在页面配置中设置 navigationStyle: custom 启用自定义导航栏
  2. 获取系统信息(状态栏高度)和胶囊按钮信息
  3. 计算导航栏高度 = 状态栏高度 + 胶囊按钮高度 + 上下间距
  4. 创建自定义导航栏组件,适配不同设备
// 获取导航栏信息
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;
}

微信小程序文件下载流程如下:

  1. 小程序端调用 wx.downloadFile 下载文件到本地临时目录
  2. 下载成功后获取文件本地路径
  3. 根据文件类型选择打开方式或保存到用户手机
  4. 对于文档类文件,可调用 wx.openDocument 打开
  5. 对于图片类文件,可保存到相册或直接显示
// 下载文件
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'
});
}
});
}
  1. 安全性

    • 敏感操作(如支付)应在后端完成
    • 使用 HTTPS 协议传输数据
    • 对用户输入进行验证和过滤
  2. 性能优化

    • 合理使用缓存机制
    • 避免频繁调用微信 API
    • 使用分包加载减少主包体积
  3. 用户体验

    • 提供加载状态提示
    • 处理网络异常情况
    • 优化页面加载速度
  1. 登录失效处理

    • 检测到登录态失效时,自动重新登录
    • 使用拦截器统一处理请求错误
  2. 支付异常处理

    • 支付完成后主动查询订单状态
    • 实现支付结果轮询机制
  3. 接口调用频率限制

    • 合理控制接口调用频率
    • 使用缓存减少不必要的请求
  4. 文件下载问题

    • 下载大文件时显示进度条
    • 处理网络中断导致的下载失败
    • 文件类型不支持时的友好提示
    • 临时文件清理机制

本文档基于 2024 年最新微信小程序 API 编写,适用于面试前快速复习。