微信小程序
数据绑定
在微信小程序中,数据绑定是通过 WXML
文件中的双大括号 {{}}
语法实现的,可以将 JavaScript 文件(.js
)中定义的数据动态渲染到页面上。以下是一个完整的例子:
1. 数据定义(在 .js
文件中)
js
// app.js 或页面.js 中
Page({
data: {
message: "Hello Mini Program!", // 文本数据
showMessage: true, // 条件渲染
list: ["Apple", "Banana", "Orange"], // 列表数据
count: 0 // 动态更新数据
},
// 点击事件处理函数
toggleMessage: function() {
this.setData({
showMessage: !this.data.showMessage // 切换布尔值
});
},
// 数字递增函数
increment: function() {
this.setData({
count: this.data.count + 1
});
}
});
2. 数据绑定(在 .wxml
文件中)
html
<!-- 页面.wxml -->
<view>
<!-- 1. 基础文本绑定 -->
<text>{{message}}</text>
<!-- 2. 条件渲染 -->
<view wx:if="{{showMessage}}">
<text>Message is visible!</text>
</view>
<view wx:else>
<text>Message is hidden.</text>
</view>
<!-- 3. 列表渲染 -->
<view wx:for="{{list}}" wx:key="index">
<text>{{index + 1}}. {{item}}</text>
</view>
<!-- 4. 事件绑定更新数据 -->
<button bindtap="toggleMessage">Toggle Message</button>
<button bindtap="increment">Count: {{count}}</button>
</view>
3. 效果说明
-
文本绑定:页面会直接显示 "Hello Mini Program!"。
-
条件渲染 :点击 "Toggle Message" 按钮会切换显示
Message is visible!
或Message is hidden.
。 -
列表渲染 :自动渲染数组
["Apple", "Banana", "Orange"]
,显示为:text1. Apple 2. Banana 3. Orange
-
动态更新:点击 "Count" 按钮时,数字会从 0 开始递增。
核心机制
- 数据驱动视图 :通过
this.setData()
更新数据,小程序会自动重新渲染相关视图。 - 条件与循环 :
wx:if
控制元素显示/隐藏,wx:for
遍历数组生成列表。 - 事件绑定 :
bindtap
绑定点击事件,调用.js
中的函数。
事件绑定
微信小程序中的事件绑定通过 bind
或 catch
前缀实现,例如 bindtap
(点击事件)、bindinput
(输入事件)等。下面是一个完整的例子,涵盖常见事件类型:
1. 事件绑定示例(.wxml
文件)
html
<!-- 页面.wxml -->
<view>
<!-- 1. 点击事件(bindtap) -->
<button bindtap="handleClick">点击我</button>
<text>点击次数:{{clickCount}}</text>
<!-- 2. 输入事件(bindinput) -->
<input
bindinput="handleInput"
placeholder="输入内容"
value="{{inputText}}"
/>
<text>输入内容:{{inputText}}</text>
<!-- 3. 长按事件(bindlongpress) -->
<button bindlongpress="handleLongPress">长按我</button>
<text>{{longPressText}}</text>
<!-- 4. 阻止事件冒泡(catchtap) -->
<view bindtap="parentTap">
<text>父容器(点击会触发)</text>
<button catchtap="childTap">子按钮(点击不会冒泡)</button>
</view>
<!-- 5. 传递自定义参数(data-*) -->
<button
bindtap="handleCustomParam"
data-id="123"
data-info="hello"
>传递参数</button>
</view>
2. 事件处理逻辑(.js
文件)
js
// 页面.js
Page({
data: {
clickCount: 0,
inputText: "",
longPressText: ""
},
// 1. 点击事件处理
handleClick() {
this.setData({
clickCount: this.data.clickCount + 1
});
},
// 2. 输入事件处理(实时更新输入内容)
handleInput(e) {
this.setData({
inputText: e.detail.value
});
},
// 3. 长按事件处理
handleLongPress() {
this.setData({
longPressText: "长按触发!"
});
},
// 4. 事件冒泡示例
parentTap() {
console.log("父容器点击事件");
},
childTap() {
console.log("子按钮点击事件(不会触发父容器的tap)");
},
// 5. 获取自定义参数
handleCustomParam(e) {
const id = e.currentTarget.dataset.id; // 123
const info = e.currentTarget.dataset.info; // "hello"
console.log("参数:", { id, info });
}
});
3. 效果说明
-
点击事件
- 点击按钮时,
clickCount
数值递增。 - 使用
bindtap
绑定点击事件。
- 点击按钮时,
-
输入事件
- 输入框内容实时同步到
inputText
。 - 通过
e.detail.value
获取输入值。
- 输入框内容实时同步到
-
长按事件
- 长按按钮显示 "长按触发!"。
- 使用
bindlongpress
绑定长按事件。
-
阻止事件冒泡
- 点击子按钮时,
catchtap
会阻止事件冒泡到父容器。 - 点击父容器其他区域仍会触发
parentTap
。
- 点击子按钮时,
-
传递自定义参数
- 通过
data-*
属性传递参数(如data-id
)。 - 在事件对象
e.currentTarget.dataset
中获取参数。
- 通过
核心机制
-
事件类型 :常用事件包括
tap
(点击)、input
(输入)、longpress
(长按)、touchstart
(触摸开始)等。 -
事件对象 :通过
e.detail
获取事件详细信息(如输入值、坐标)。 -
冒泡控制:
bind
允许事件冒泡(如bindtap
)。catch
阻止事件冒泡(如catchtap
)。
-
参数传递 :使用
data-*
属性向事件处理函数传递自定义数据。
微信小程序的模板语法类指令
一、列表渲染指令
-
**
wx:for
**用于循环渲染数组或对象,默认通过
{{item}}
和{{index}}
访问元素及索引。html<view wx:for="{{list}}">{{index}}: {{item}}</view>
-
**
wx:for-item
**自定义循环元素的变量名(默认
item
)。html<view wx:for="{{list}}" wx:for-item="book">{{book.name}}</view>
-
**
wx:for-index
**自定义循环索引的变量名(默认
index
)。 -
**
wx:key
**指定列表项的唯一标识符(如
id
或*this
),优化渲染性能。
二、条件渲染指令
-
**
wx:if
/wx:elif
/wx:else
**根据条件动态控制元素的显示与隐藏,通过条件判断直接操作 DOM 结构。
html<view wx:if="{{show}}">显示内容</view> <view wx:else>隐藏内容</view>
-
**
hidden
属性**通过布尔值控制元素显隐(仅切换 CSS 的
display
属性),适用于频繁切换的场景。html<view hidden="{{isHidden}}">通过 hidden 属性控制显隐</view>
三、模板定义与引用指令
-
**
template
标签**定义可复用的代码片段,通过
name
属性命名模板。html<template name="msgItem"> <view>{{index}}: {{msg}}</view> </template>
-
**
is
属性**动态引用模板,结合
data
属性传递所需数据。html<template is="msgItem" data="{{...item}}" />
-
动态选择模板
通过三元表达式动态渲染不同模板,例如:
html<template is="{{item % 2 === 0 ? 'even' : 'odd'}}" />
四、其他辅助指令
-
**
block
标签**包裹多个元素但不渲染自身,常用于结合条件或循环指令。
html<block wx:if="{{flag}}"> <view>A</view> <view>B</view> </block>
总结
指令类型 | 核心指令/属性 | 作用场景 |
---|---|---|
列表渲染 | wx:for , wx:key |
遍历数组/对象 |
条件渲染 | wx:if , hidden |
动态控制元素显隐 |
模板定义与引用 | template , is , data |
复用代码片段 |
辅助标签 | block |
包裹多元素且不渲染自身 |
注意事项
- 数据绑定 :所有属性值或内容需通过
{{}}
绑定动态数据(如<input value="{{text}}" />
),支持简单表达式(如{{num + 1}}
)。 - 性能优化 :列表渲染中必须使用
wx:key
提升渲染效率。 - 模板复用 :
template
适用于跨页面或组件复用复杂结构。
微信小程序的界面跳转
一、基础跳转方式
-
声明式导航(
<navigator>
组件) -
基本语法 :通过
url
属性指定目标页面路径,open-type
定义跳转类型56。html<!-- 保留当前页面跳转(可返回) --> <navigator url="/pages/detail/detail" open-type="navigate">跳转详情页</navigator> <!-- 关闭当前页面跳转(不可返回) --> <navigator url="/pages/detail/detail" open-type="redirect">重定向跳转</navigator>
-
参数传递 :通过 URL 查询字符串传递参数,目标页面在
onLoad
中通过options
接收68。html<navigator url="/pages/detail/detail?id=123&name=test">带参跳转</navigator>
-
-
编程式导航(API 调用)
-
常用 API:
方法 作用 特点 wx.navigateTo
保留当前页面跳转,可返回 最多支持 10 层页面栈 wx.redirectTo
关闭当前页面跳转,不可返回 页面栈减少一层 wx.switchTab
跳转至 tabBar
页面,关闭其他非tabBar
页面仅适用于配置了 tabBar
的页面wx.reLaunch
关闭所有页面并打开新页面 重置页面栈 wx.navigateBack
返回上一级或多级页面 通过 delta
参数控制返回层级 -
代码示例:
js// 保留跳转(带参数) wx.navigateTo({ url: '/pages/detail/detail?key=value' }); // 关闭当前页跳转 wx.redirectTo({ url: '/pages/home/home' });
-
二、高级跳转场景
-
跨小程序跳转
-
实现方式 :使用
wx.navigateToMiniProgram
,需在目标小程序的app.json
中配置关联34。jswx.navigateToMiniProgram({ appId: '目标小程序AppID', path: 'pages/index/index', // 目标页面路径 success: () => console.log('跳转成功') });
-
限制:需目标小程序开启关联权限,且用户已授权。
-
-
H5 页面跳转小程序
-
步骤:
- 通过第三方平台(如天天外链)生成跳转链接,配置目标小程序路径和参数。
- 将链接嵌入 H5 页面,用户点击后触发微信内置浏览器跳转逻辑。
-
示例配置:
js// 生成跳转链接(第三方工具) const url = 'https://外链域名/path?appid=小程序ID&path=目标页面';
-
-
App 跳转小程序
-
条件:
- 已认证的开放平台账号可跳转任意合法小程序。
- 未认证账号仅支持跳转同一开放平台下的小程序。
-
实现 :调用微信 SDK 的
WXLaunchMiniProgram
方法。
-
三、常见问题与注意事项
-
页面栈管理
- 使用
getCurrentPages()
获取当前页面栈,避免超过 10 层导致跳转失败。 reLaunch
会清空页面栈,慎用于高频操作场景。
- 使用
-
**
tabBar
页面限制**switchTab
仅支持跳转已在app.json
的tabBar
配置中声明的页面。- 跳转
tabBar
页面时不可携带参数。
-
外部跳转兼容性
- H5 或 App 跳转需确保目标小程序的
app.json
已配置通用链接(Universal Link)。 - 部分安卓机型需手动开启"允许从外部应用跳转"权限。
- H5 或 App 跳转需确保目标小程序的
四、最佳实践
-
统一路由管理
-
封装全局路由工具类,集中处理路径拼接、参数编码等逻辑。
js// utils/router.js export const navigateTo = (path, params) => { const query = new URLSearchParams(params).toString(); wx.navigateTo({ url: `${path}?${query}` }); };
-
-
安全性控制
- 对跳转参数进行合法性校验,防止注入攻击。
- 跨小程序跳转时校验目标
appId
白名单。
-
性能优化
- 减少
navigateTo
层级,复杂流程优先使用redirectTo
或reLaunch
。 - 预加载目标页面数据,提升跳转后渲染速度
- 减少
微信小程序的生命周期
一、应用生命周期
通过 App()
函数注册,在 app.js
中定义,核心函数包括:
-
onLaunch
小程序初始化时触发(仅执行一次),用于获取启动参数或初始化全局数据。 -
onShow
小程序启动或从后台进入前台时触发(如用户重新打开小程序)。 -
onHide
小程序从前台进入后台时触发(如用户点击右上角关闭)。 -
onError
脚本执行错误或 API 调用失败时触发,用于错误监控。
二、页面生命周期
在页面 .js
文件中定义,通过 Page()
函数注册,关键函数如下:
-
onLoad
页面加载时触发(仅一次),用于获取页面参数或发起网络请求。 -
onShow
页面显示/切入前台时触发(如切换回该页面)。 -
onReady
页面初次渲染完成时触发(仅一次),用于操作 DOM 或执行交互逻辑。 -
onHide
页面隐藏/切入后台时触发(如跳转至其他页面)。 -
onUnload
页面销毁时触发(如关闭页面或路由跳转离开),用于清理定时器等资源。
三、组件生命周期
通过 Component()
函数注册,在组件 .js
中定义,主要函数包括:
-
created
组件实例创建时触发,但尚未挂载到页面节点树。 -
attached
组件被添加到页面节点树时触发,可访问页面数据。 -
detached
组件从页面节点树移除时触发,用于资源释放。 -
pageLifetimes
监听组件所在页面的生命周期(如页面显示/隐藏)。
四、运行机制与状态切换
- 冷启动
首次打开或销毁后重新启动,触发onLaunch
。 - 热启动(后台切前台)
从后台恢复至前台,触发onShow
。 - 挂起
后台持续 5 秒无操作后,JS 线程停止,保留内存状态。 - 销毁
长时间未使用(30 分钟)或系统资源不足时触发,完全终止运行。
生命周期执行顺序示例
text
应用启动 → onLaunch → 页面加载 → onLoad → onShow → onReady
切换后台 → 应用 onHide → 页面 onHide
重新进入 → 应用 onShow → 页面 onShow
销毁页面 → 页面 onUnload
注意事项
- 性能优化 :避免在
onShow
中频繁执行耗时操作,优先使用onLoad
初始化数据。 - 数据同步 :页面隐藏时(
onHide
)保存临时数据,避免丢失。 - 组件复用 :利用
detached
清理组件内事件监听,防止内存泄漏。
微信小程序的组件使用
一、组件核心概念
-
作用与优势
- 代码复用:封装 UI 和逻辑,避免重复开发。
- 独立性:组件拥有独立的数据、样式和生命周期,与页面解耦。
- 维护性:复杂业务拆分为多个组件,降低代码耦合度。
-
组件与页面的区别
特性 页面 组件 文件结构 包含 .json
文件声明页面需在 .json
中声明"component": true
生命周期 onLoad
,onShow
等created
,attached
等使用方式 通过路由跳转访问 需在页面或父组件中引入
二、组件创建与使用
-
创建自定义组件
-
文件结构:
textcomponents/ my-component/ my-component.js // 组件逻辑 my-component.wxml // 组件模板 my-component.wxss // 组件样式 my-component.json // 组件配置
-
配置
my-component.json
:json{ "component": true, "usingComponents": {} // 可嵌套其他组件 }
-
-
在页面中引入组件
-
声明组件 (页面
.json
中):json{ "usingComponents": { "my-component": "/components/my-component/my-component" } }
-
使用组件 (页面
.wxml
中):html<my-component prop-a="{{value}}" bind:myevent="handleEvent" />
-
三、组件核心配置
-
数据与属性
-
内部数据 (
data
):jsComponent({ data: { count: 0 } })
-
外部属性 (
properties
):支持类型校验和默认值:jsComponent({ properties: { propA: { type: String, value: 'default' }, propB: Number // 简写 } })
-
-
事件通信
-
触发事件(子组件向父组件):
js// 子组件中触发 this.triggerEvent('myevent', { detail: data }, { bubbles: false })
-
监听事件(父组件中):
html<!-- 父组件.wxml --> <my-component bind:myevent="handleEvent" />
-
-
插槽(Slot)
-
默认插槽:
html<!-- 组件模板.wxml --> <view class="container"> <slot></slot> </view> <!-- 使用组件 --> <my-component> <text>插入的内容</text> </my-component>
-
具名插槽:
html<!-- 组件模板.wxml --> <slot name="header"></slot> <slot name="footer"></slot> <!-- 使用组件 --> <my-component> <text slot="header">头部内容</text> <text slot="footer">底部内容</text> </my-component>
-
四、高级功能
-
组件间关系
-
定义关系 (
relations
):jsComponent({ relations: { './child-component': { type: 'child', linked(target) { console.log('子组件已链接', target) } } } })
-
访问关联组件 :通过
this.getRelationNodes('./child-component')
。
-
-
外部样式类
-
暴露样式类 (组件
.js
中):jsComponent({ externalClasses: ['custom-class'] // 允许外部传入样式类 })
-
使用外部样式(父组件中):
html<my-component custom-class="my-style" />
-
-
生命周期
生命周期 触发时机 created
组件实例刚创建(不能调用 setData
)attached
组件完全初始化并插入页面 DOM 树 detached
组件从页面 DOM 树移除
五、常见问题与解决
问题场景 | 解决方案 |
---|---|
组件样式不生效 | 检查组件 .json 是否声明 "styleIsolation": "apply-shared" (启用样式隔离) |
父组件无法监听子组件事件 | 确认 triggerEvent 的第三个参数未设置 bubbles: false (默认向上冒泡) |
插槽内容渲染异常 | 确保组件 .wxml 中正确使用 <slot name="xxx"> 并与使用端 slot 属性匹配 |
组件间数据不同步 | 使用 observers 监听属性变化:properties: { propA: { observer: function } } |
六、最佳实践
-
性能优化
-
避免在
attached
生命周期中执行耗时操作。 -
使用
pureDataPattern
标记纯数据字段,减少不必要的渲染:jsComponent({ options: { pureDataPattern: /^_/ // 以 _ 开头的字段为纯数据 }, data: { _internalData: '不参与渲染' } })
-
-
组件复用性设计
-
通过
behaviors
复用代码逻辑:js// behavior.js module.exports = Behavior({ methods: { sharedMethod() { /* ... */ } } }) // 组件中引入 Component({ behaviors: [require('./behavior.js')] })
-
微信小程序的传值
一、页面间传值
1. URL 参数传递
-
适用场景:页面跳转时传递简单参数(如 ID)。
-
实现方式:
js// 跳转时传参(发送方) wx.navigateTo({ url: '/pages/detail/detail?id=123&name=test' }) // 接收参数(接收方 detail 页面的 onLoad 函数) onLoad(options) { const { id, name } = options // id='123', name='test' }
-
注意 :参数需拼接为字符串,复杂数据需用
JSON.stringify
序列化。
2. 本地存储
-
适用场景:跨页面持久化数据(如用户 Token)。
-
实现方式:
js// 存储数据 wx.setStorageSync('key', { a: 1 }) // 读取数据(任意页面) const data = wx.getStorageSync('key')
-
注意 :避免存储过大或敏感数据,异步 API 可用
wx.setStorage
。
二、组件间传值
1. 父传子:Properties
-
适用场景:父组件向子组件传递静态或动态数据。
-
实现方式:
html<!-- 父组件使用子组件 --> <child-component prop-a="{{value}}" /> <!-- 子组件定义 properties --> Component({ properties: { propA: { type: String, value: '默认值' } } })
2. 子传父:自定义事件
-
适用场景:子组件向父组件传递数据或触发行为。
-
实现方式:
js// 子组件触发事件 this.triggerEvent('eventName', { data: 123 }) // 父组件监听事件 <child-component bind:eventName="handleEvent" /> // 父事件处理函数 handleEvent(e) { const data = e.detail.data // 123 }
3. 兄弟组件传值
- 方法一:通过父组件中转(结合 Properties + 事件)。
- 方法二:使用全局事件总线(需自行封装或使用库)。
三、全局数据管理
1. 全局变量(app.js)
-
适用场景:多页面共享数据(如用户信息)。
-
实现方式:
js// app.js 中定义 App({ globalData: { token: 'xxxx' } }) // 任意页面或组件获取 const app = getApp() console.log(app.globalData.token)
2. 状态管理工具
- 推荐库 :
wechat-weapp-redux
、mobx-miniprogram
。 - 适用场景:复杂应用的多组件状态同步。
四、其他传值方式
1. 页面栈传值
-
适用场景:返回上一页时传递数据。
-
实现方式:
js// 获取页面栈实例 const pages = getCurrentPages() const prevPage = pages[pages.length - 2] // 上一页实例 // 修改上一页数据 prevPage.setData({ backData: '123' }) // 返回上一页 wx.navigateBack()
2. 通过 Dataset 传递
-
适用场景:事件触发时传递元素自定义数据。
-
实现方式:
html<view data-id="123" bindtap="handleTap"></view> <script> handleTap(e) { const id = e.currentTarget.dataset.id // '123' } </script>
五、最佳实践总结
场景 | 推荐方法 | 注意事项 |
---|---|---|
简单页面跳转传参 | URL 参数 | 数据量小,避免复杂结构 |
父子组件通信 | Properties + 自定义事件 | 避免滥用双向绑定 |
跨页面持久化数据 | 本地存储(wx.setStorageSync ) |
定期清理过期数据 |
全局共享状态 | app.globalData 或状态管理库 |
复杂项目优先使用 Redux/MobX |
兄弟组件通信 | 父组件中转或全局事件 | 结构复杂时考虑状态管理工具 |
关键注意事项
- 性能优化:避免频繁传递大数据,优先通过 ID 查询详情。
- 数据安全:敏感数据(如密码)禁止明文存储或传递。
- 内存管理 :页面销毁时(
onUnload
)及时清理无用数据。
微信登录流程
一、流程图解
二、分步流程说明
-
用户触发登录操作
用户点击登录按钮,小程序前端调用
wx.login()
接口获取临时登录凭证code
,有效期 5 分钟。 -
发送 code 至开发者服务器
小程序通过 HTTPS 请求将
code
发送至开发者服务器,用于换取用户唯一标识openid
和会话密钥session_key
。 -
换取 session_key 和 openid
开发者服务器调用微信接口
https://api.weixin.qq.com/sns/jscode2session
,提交以下参数:appid
:小程序 IDsecret
:小程序密钥js_code
:接收到的code
grant_type
:固定为authorization_code
微信服务器返回openid
(用户唯一标识)和session_key
(会话密钥)。
-
生成自定义登录态
开发者服务器根据业务需求生成自定义登录态(如 JWT Token),并与
openid
绑定存储至数据库或缓存系统。 -
返回 Token 至小程序
开发者服务器将 Token 返回给小程序前端,前端通过
wx.setStorageSync
存储至本地缓存,后续请求携带 Token 验证身份。
三、关键注意事项
-
安全性要求
session_key
严禁传输至客户端,仅用于服务端解密用户敏感数据(如手机号)。- Token 需设置有效期,建议结合
session_key
过期时间(默认 30 分钟)。
-
用户信息获取
- 若需获取用户昵称、头像等,需调用
wx.getUserProfile
并引导用户授权。 - 加密数据(如
encryptedData
)需在服务端用session_key
解密。
- 若需获取用户昵称、头像等,需调用
-
登录态维护
- 建议在
app.globalData
或本地缓存中存储 Token,定期检查过期时间并自动续期。
- 建议在
四、常见问题处理
| 问题 | 解决方案 |
|--------------------|------------------------------|---|
| code
失效(超时/重复使用) | 重新调用 wx.login()
获取新 code
| |
| session_key
泄露风险 | 限制接口调用频率,避免明文传输敏感数据 |
| 多端登录冲突 | 基于 openid
绑定设备标识,强制下线旧设备 |
微信支付流程
一、流程图解
二、分步流程说明
-
前置条件
- 小程序需完成企业认证,并关联微信支付商户号(主体一致)
- 开发者服务器需配置
appid
、mch_id
(商户号)、API 密钥等敏感信息
-
核心步骤
① 用户下单
- 用户在小程序内选择商品,点击「支付」按钮触发支付流程
- 小程序前端调用
wx.login()
获取code
,发送至后端换取用户openid
(用户唯一标识)
② 生成预付单
-
开发者服务器调用微信支付「统一下单接口」,提交以下参数:
openid
(用户标识)body
(商品描述)total_fee
(金额,单位为分)notify_url
(支付结果回调地址)
-
微信支付返回
prepay_id
(预付单标识)和签名参数(paySign
、nonceStr
等)
③ 调起支付控件
-
小程序前端通过
wx.requestPayment
调起支付界面,携带以下参数:timeStamp
(时间戳)nonceStr
(随机字符串)package
(格式:prepay_id=xxx
)signType
(签名类型,默认MD5
或HMAC-SHA256
)paySign
(服务端生成的签名)
④ 支付结果处理
- 支付成功:微信支付异步通知开发者服务器(需验证签名和金额一致性)
- 支付失败:前端显示错误提示(如余额不足、网络异常等)
三、前端代码案例
用户触发支付按钮
js
// pages/pay/pay.js
Page({
handlePayment() {
// 1. 获取用户openid(需提前登录)
const openid = wx.getStorageSync('openid');
// 2. 向后端请求支付参数
wx.request({
url: 'https://your-api.com/create-pay-order',
method: 'POST',
data: {
productId: '123', // 商品ID
totalFee: 100, // 金额(单位:分)
openid: openid // 用户唯一标识
},
success: (res) => {
// 3. 接收后端返回的支付参数
const payParams = res.data;
this.launchPayment(payParams);
},
fail: (err) => {
wx.showToast({ title: '订单创建失败', icon: 'none' });
}
});
},
// 调起微信支付
launchPayment(payParams) {
wx.requestPayment({
timeStamp: payParams.timeStamp, // 时间戳(秒级)
nonceStr: payParams.nonceStr, // 随机字符串
package: payParams.package, // prepay_id=xxx
signType: 'RSA', // 2025年起强制使用RSA签名
paySign: payParams.paySign, // 服务端生成的签名
success: (res) => {
// 4. 支付成功处理
wx.showToast({ title: '支付成功' });
this.checkOrderStatus();
},
fail: (err) => {
// 5. 支付失败处理
const errMsg = err.errMsg || '支付中断';
wx.showToast({ title: errMsg, icon: 'none' });
}
});
},
// 查询最终订单状态(兜底逻辑)
checkOrderStatus() {
wx.request({
url: 'https://your-api.com/check-order',
method: 'POST',
data: { orderId: 'ORDER_123' },
success: (res) => {
if (res.data.status === 'SUCCESS') {
wx.navigateTo({ url: '/pages/success/success' });
}
}
});
}
});
四、核心代码说明
1. 支付参数获取
参数 | 来源 | 示例值 |
---|---|---|
timeStamp |
服务端生成(秒级时间戳) | "1717027890" |
nonceStr |
服务端生成的随机字符串 | "a1b2c3d4e5f6g7h8" |
package |
微信返回的预付单标识 | "prepay_id=wx1234567890" |
signType |
固定为 RSA (2025 年强制要求) |
"RSA" |
paySign |
服务端使用商户私钥生成的 RSA 签名 | "MIIEvQIBADANBg..." |
2. 安全校验要点
js
// 支付参数校验示例(前端辅助验证)
function validateParams(payParams) {
const requiredFields = ['timeStamp', 'nonceStr', 'package', 'paySign'];
return requiredFields.every(field => payParams[field]);
}
// 调用支付前验证参数
if (!validateParams(payParams)) {
wx.showToast({ title: '支付参数异常', icon: 'none' });
return;
}
五、支付流程注意事项
-
签名安全
- 服务端必须使用 微信商户平台 的 API 私钥生成
paySign
- 禁止在前端生成签名或传输私钥
- 服务端必须使用 微信商户平台 的 API 私钥生成
-
支付结果兜底
- 即使收到支付成功回调,仍需通过
checkOrderStatus
查询最终状态 - 处理场景:网络中断、用户关闭小程序等异常
- 即使收到支付成功回调,仍需通过
-
用户体验优化
js// 支付超时处理(30秒限制) setTimeout(() => { wx.showToast({ title: '支付超时,请重试', icon: 'none' }); }, 30000);
-
错误码处理
js// 常见错误码处理 const errorMap = { 'requestPayment:fail cancel': '用户取消支付', 'requestPayment:fail no auth': '未授权支付功能' }; wx.requestPayment({ // ... fail: (err) => { const message = errorMap[err.errMsg] || '支付失败'; wx.showToast({ title: message, icon: 'none' }); } });
六、测试建议
-
沙箱环境
js// 测试环境开关 const isTestEnv = true; // 上线前设为false const API_BASE_URL = isTestEnv ? 'https://test.your-api.com' : 'https://prod.your-api.com';
微信小程序分包
一、分包背景与目的
-
核心限制
- 整个小程序所有分包大小不超过
30M
(服务商代开发的小程序不超过20M
) - 单个分包/主包大小不能超过
2M
独立分包
可独立运行,无需依赖主包加载
- 整个小程序所有分包大小不超过
-
主要优势
- 优化首次启动速度,仅下载主包36
- 支持多团队协作开发,按功能模块拆分代码27
- 突破主包体积限制,扩展小程序功能16
二、分包项目构成
- 目录结构示例
text
├── app.js # 主包入口文件
├── app.json # 分包配置(含 subpackages 字段)
├── pages/ # 主包页面(含 TabBar 页面)
│ ├── index
│ └── profile
├── subPackage1/ # 分包1
│ └── pages/
│ ├── cart
│ └── detail
└── subPackage2/ # 分包2
└── pages/
├── order
└── payment
- 主包 :必须包含启动页(如
pages/index
)和 TabBar 页面 - 分包:独立目录存放页面、组件及私有资源
- 配置文件
app.json
json
{
"pages": ["pages/index", "pages/profile"],
"subpackages": [
{
"root": "subPackage1", // 分包根目录
"name": "shopModule", // 分包别名(可选)
"pages": ["pages/cart", "pages/detail"],
"independent": false // 是否独立分包
},
{
"root": "subPackage2",
"pages": ["pages/order", "pages/payment"]
}
]
}
root
:分包根目录路径independent
:设置为true
时,分包可独立运行
三、分包加载规则与限制
-
加载规则
- 启动小程序时仅下载主包,访问分包页面时动态加载对应子包
- 独立分包首次访问时直接加载,无需主包依赖
-
体积限制
类型 大小限制 说明 主包 ≤ 2MB 含公共资源和 TabBar 页面 单个分包 ≤ 2MB 独立分包也需遵守 所有分包总和 整个小程序所有分包大小不超过 30M(服务商代开发的小程序不超过 20M) -
预加载分包
- 通过
preloadRule
配置提前加载高频使用的分包
- 通过
json
{
"preloadRule": {
"pages/index": {
"network": "all",
"packages": ["subPackage1"] // 访问主包 index 页时预加载分包1
}
}
}
四、分包开发注意事项
-
路径配置
- 分包内页面路径需相对于
root
目录(如subPackage1/pages/cart
) - 跨分包跳转需使用绝对路径:
/subPackage1/pages/cart
- 分包内页面路径需相对于
-
资源引用
- 分包私有资源(如图片、样式)需存放于分包目录内
- 公共资源建议放置在主包,避免重复打包
-
常见错误
- TabBar 页面放在分包中:必须保留在主包
- 分包路径配置错误:导致页面无法加载或白屏
五、优化策略
-
公共资源提取
- 将通用组件、工具库(如
utils
)放置在主包 - 使用
npm
或Vant
等第三方库时,优先选择按需加载
- 将通用组件、工具库(如
-
独立分包使用场景
- 营销活动页、支付流程等高频独立功能
- 示例:独立分包中配置
"independent": true
实现快速启动
-
分包大小监控
- 通过开发者工具「代码依赖分析」检测分包体积
- 超限解决方案:压缩图片、删除冗余代码、拆分更小子包
微信小程序 npm 构建详解(以 Vant Weapp 为例)
一、构建流程
-
初始化 npm 项目
bashcd /your-project-path # 进入项目根目录 npm init -y # 自动生成 package.json :ml-citation{ref="4,6" data="citationList"}
- 云开发项目需进入
miniprogram
子目录执行初始化。
- 云开发项目需进入
-
安装 Vant Weapp
bashnpm i @vant/weapp -S --production
-
配置文件调整
-
删除
app.json
中的"style": "v2"
:避免基础组件样式冲突。 -
修改
project.config.json
:json{ "setting": { "packNpmManually": true, "packNpmRelationList": [{ "packageJsonPath": "./package.json", "miniprogramNpmDistDir": "./miniprogram/" # 指向项目根目录 :ml-citation{ref="1,4" data="citationList"} }] } }
-
-
构建 npm
- 微信开发者工具中点击 工具 > 构建 npm ,成功生成
miniprogram_npm
目录。
- 微信开发者工具中点击 工具 > 构建 npm ,成功生成
二、Vant Weapp 集成
-
引入组件
-
全局配置(
app.json
) :json{ "usingComponents": { "van-button": "@vant/weapp/button/index" # 路径包含 miniprogram_npm :ml-citation{ref="2,5" data="citationList"} } }
-
页面级配置(页面
.json
文件) :按需引入减少体积57。
-
-
全局样式加载
css/* app.wxss */ @import '/miniprogram_npm/@vant/weapp/common/index.wxss'; # 引入 Vant 基础样式 :ml-citation{ref="1,6" data="citationList"}
-
组件使用示例
html<!-- 页面.wxml --> <van-button type="primary" bind:click="handleClick">提交</van-button>
三、常见问题与解决
问题 | 解决方案 |
---|---|
构建后无 miniprogram_npm 目录 |
检查 project.config.json 的 miniprogramNpmDistDir 路径是否匹配项目结构 |
npm install 报错权限不足** |
以管理员身份运行开发者工具,或手动创建 package.json 后重试 |
组件样式不生效 | 确认已删除 app.json 的 "style": "v2" 并正确引入全局样式文件 |
**提示 Component not found ** |
检查组件路径是否为 @vant/weapp/xxx/index (构建后自动映射到 miniprogram_npm ) |
四、最佳实践
-
版本控制
- 固定依赖版本(如
@vant/weapp@2.15.0
),避免自动升级导致兼容性问题。 - 定期执行
npm outdated
检测更新。
- 固定依赖版本(如
-
分包优化
- 主包存放公共组件,独立分包单独声明依赖(需在分包目录创建
package.json
)。
- 主包存放公共组件,独立分包单独声明依赖(需在分包目录创建
-
性能监控
- 使用开发者工具 代码体积分析 功能,对超过 2MB 的分包进行代码拆分或压缩。