小程序页面之间怎么传参?3 种常见做法一次讲清

编程狮(w3cschool.cn) 2026-10-12 17:39:19 浏览数 (51)
反馈

把商品详情页的商品 id 传到下单页、把登录态带进个人中心、把筛选条件交给列表页——小程序里页面之间传参几乎是每个项目都会碰到的需求。一句话结论:最常用的是在跳转链接后面拼 URL query 参数,适合少量、一次性的数据;需要跨多个页面共享的开关或用户信息,挂在 app 实例的 globalData 全局变量上最省事;体积较大或刷新后还想保留的数据,用本地缓存 Storage 最稳妥。三种做法没有绝对的优劣,关键看数据大小、是否要持久、涉及几个页面。下面先用一张决策表看清边界,再逐个给出可复现的代码和容易踩的坑。

小程序 页面传参 3 种做法封面

先看结论

你的传参场景 首选做法 适合的数据量 是否持久
页面跳转带少量一次性数据 URL query 参数 小(几十字符内) 否,刷新即丢
跨多个页面共享状态 globalData 全局变量 小到中 否,进程内有效
较大或需刷新后保留 本地缓存 Storage 大(对象、列表) 是
仅当前页内部使用 页面 data 字段 任意 否

记住一条铁律:大对象或要持久化的数据用 Storage,小数据用 URL;globalData 只放真正全局、进程级的共享状态,别把它当万能仓库,否则状态来源会乱、出问题难追。

一、小程序的页面栈与传参本质

要搞懂小程序 页面传参,先得理解小程序的页面栈。小程序运行时在内存里维护一个页面栈,调用 wx.navigateTo 会往栈里压入新页面,调用 wx.navigateBack 会弹出栈顶回到上一页。栈的存在带来两个直接后果:第一,新页面是通过「入栈」出现的,所以它只能从跳转动作里接收数据;第二,页面被弹出栈后,除非你主动存到别处,否则它身上的临时数据随之消失。每个页面在 onLoad 生命周期里拿到的,正是「跳转时 URL 后面的 query 参数」,也就是常说的 URL 参数,这是官方设计里最正统的入口传参通道。微信小程序开发文档 把页面栈、路由 API 和生命周期讲得很细,建议先过一遍再动手,免得后面把几种传参方式混在一起用。

页面栈的存在还决定了传参的两条边界。第一条边界是「栈内和栈外」:navigateTo 是压栈、能拿到 query;而 reLaunch、redirectTo 会替换或重启页面栈,query 依然能用,但返回逻辑不同,reLaunch 会清掉整个栈。第二条边界是「一次性和长期」:query 只活在这一次跳转里,页面关闭或小程序销毁后就没了;如果你想让数据在多个页面之间反复读写,就不能只依赖 query,得换共享通道。配置页面路由时,app.json 的 pages 字段决定哪些页面存在、哪个是首页,页面路径本身不支持动态段,所有路径参数都必须以 query 形式拼在 URL 后面,例如 /pages/detail/detail?id=123。理解页面栈和路由模型,是选对传参方式的前提。

除了 query,navigateTo 还提供了 events 和 eventChannel 机制,可以在跳转时绑定事件、在目标页通过 this.getOpenerEventChannel() 拿到通道,用它回传数据或传递函数,适合 URL 装不下的大对象。不过 eventChannel 只在 navigateTo 场景有效,且写法比 query 复杂,新手先用 query 和 globalData 打底更稳。下面先放一张决策图,帮你对着场景直接挑做法。

小程序页面传参方式选择决策图

二、URL query 参数:跳转时拼在链接后面

页面传参里最高频、最稳的就是 URL 参数。做法很简单:在调用 navigateTo 时,把数据以 key=value 的形式拼在 url 后面,多个参数用 & 连接;目标页面在 onLoad(options) 的 options 里就能拿到。它适用于所有「从 A 到 B 只传一次」的场景,比如列表点进详情、表单提交后跳结果页、分享卡片带邀请码。支付宝小程序教程 给出的路由示例和微信几乎一致,API 只是把 wx 换成 my,逻辑可以直接对照迁移,所以下面代码以微信为例,你换成 my 前缀即可跑在支付宝上。

// A 页面:跳转到详情页,带商品 id 和来源
wx.navigateTo({
  url: '/pages/detail/detail?id=123&from=list'
})

// B 页面 detail.js:在 onLoad 里接收
Page({
  onLoad(options) {
    console.log(options.id)   // "123"
    console.log(options.from) // "list"
  }
})

正常运行时,B 页面 onLoad 的 options 会原样拿到字符串形式的参数;你预期拿到的是字符串,所以数字要先做类型转换,布尔值也要自己 parse。失败分支最常出现在两种地方。第一种是参数里有特殊字符或中文没编码,URL 会被截断或解析错,修复办法是对值调用 encodeURIComponent,到目标页再用 decodeURIComponent 解回来。第二种是参数太长,超出小程序对 URL 长度的限制,此时命令本身不报错但数据会丢,排查时检查 url 实际拼接结果即可定位。版本方面,基础库 2.x 对 query 的处理稳定,老版本差异主要在 API 名称上。HTML 教程 里关于 URL 编码与查询字符串的章节,能帮你把编码规则一次弄懂,避免中文乱码这类经典失败。

// 带中文或特殊字符时务必编码
const name = encodeURIComponent('无线机械键盘')
wx.navigateTo({
  url: `/pages/detail/detail?name=${name}`
})
// 目标页
Page({
  onLoad(options) {
    const name = decodeURIComponent(options.name)
  }
})

除了 navigateTo,redirectTo 和 reLaunch 也能带 query,区别在栈行为:redirectTo 关闭当前页再开新页,没有返回上一层;reLaunch 直接重置整个页面栈,常用于退出登录后回到首页。选择哪个路由 API 取决于你想不想保留返回链,但传参语法完全一致,都是把数据拼在 url 后面。URL 参数对比本地缓存,胜在直观、无副作用、不占存储;边界是它只能传字符串、体积有限、且刷新即失。当你发现需要传对象或数组,就该换下面的方式。

三、globalData 全局变量:跨页面共享状态

当数据要在多个页面之间反复读写,比如用户登录信息、当前城市、主题开关,挂在 app 实例的 globalData 上最省事。globalData 是小程序全局唯一的共享内存,任何页面都能通过 getApp() 读写它,不需要经过路由,也不需要序列化。它适用于「进程级、跨页面、读多写少」的共享状态。JavaScript 教程 里关于对象和全局作用域的章节,能帮你看清为什么 globalData 本质是挂在一个单例对象上——它和浏览器里 window 上挂变量的思路一脉相承,只是作用范围限定在小程序进程内。

// app.js
App({
  globalData: {
    token: '',
    city: '北京'
  }
})

// 任意页面读取 / 写入
const app = getApp()
app.globalData.token = 'abc123'
console.log(app.globalData.city)

运行后,任意页面调用 getApp() 都能立即拿到最新值;你预期的是「改一处、处处生效」,不需要层层传参。失败分支主要在刷新场景:globalData 存在内存里,小程序被销毁重建后数据归零,所以别把需要持久的东西只放这里。修复办法是把关键数据同时写进 Storage,启动时再读回 globalData,做成「内存热数据 + 本地冷备份」的组合。安全方面要注意,globalData 是全局可写的,多人协作时状态来源容易混乱,建议集中封装读写函数而不是到处直接赋值,这样以后排查谁改了状态会轻松很多。性能上它只是内存读写,几乎零成本,但字段太多会拖慢冷启动排查,保持精简即可。CSS 教程 虽偏样式,但其中「全局变量与局部作用域」的思路和这里异曲同工,可作为类比理解为什么全局状态要克制使用。

一个常见坑是过早调用 getApp():在 App 的 onLaunch 还没执行完时,其它页面的 onLoad 里就读 globalData,可能拿到的是默认值。更稳的做法是在 app 里用异步登录拿到 token 后再写入,页面侧读取前先判断是否为空。globalData 的使用边界很清楚:只放真正全局、进程级的状态,别把一次跳转就能解决的参数也塞进来,否则页面间的数据流会变难追、回归 bug 难定位。下面看第三种、也是唯一能持久化的方式。

四、本地缓存 Storage:大对象与持久化

当数据体积较大(比如一整个商品对象、购物车列表)或希望小程序重启后还在,就用本地缓存 Storage。它是最稳的持久通道,API 是 wx.setStorage / wx.getStorage,同步版是 wx.setStorageSync / wx.getStorageSync。它适用于「需要跨会话保留」或「对象较大不适合塞 URL」的场景,比如记住登录 token、缓存首页频道配置、暂存未提交的表单。Storage 存的是字符串,对象要先 JSON.stringify 再存、取时再 JSON.parse,这一步新手最容易漏。

// 存一个商品对象
const goods = { id: 123, name: '无线机械键盘', price: 299 }
wx.setStorageSync('currentGoods', JSON.stringify(goods))

// 取出来用
const raw = wx.getStorageSync('currentGoods')
const goods2 = JSON.parse(raw)
console.log(goods2.name)

运行后,数据会写进小程序本地存储,重启小程序再 getStorageSync 依然在;你预期拿到的是还原后的对象。失败分支有几类。第一类是忘记 JSON 序列化,直接存对象,部分基础库会报错或存成 [object Object],修复就是先 stringify 再存。第二类是 key 冲突,不同模块用了相同 key 互相覆盖,排查时打印所有 key(wx.getStorageInfoSync().keys)即可定位是谁占了名。第三类是容量超限,单个小程序 Storage 上限约 10MB,超限会写失败,此时需要清理或换服务端存储。安全上注意 Storage 是明文落在本机,别存密码、token 等敏感字段,敏感信息走服务端会话更稳妥。命令层面,wx.removeStorageSync 可以单独删某个 key,wx.clearStorageSync 会清空全部,后者慎用,别在调试时误清空用户数据。

Storage 也有异步版本 wx.setStorage 和 wx.getStorage,传入 success/fail 回调或用 Promise 形式,适合在页面加载时异步预取缓存、不阻塞首屏。实际项目里常把「用户资料」「首页配置」这类读多写少的数据缓存起来,启动时先渲染缓存、再拉服务端最新覆盖,既快又稳。注意 key 的命名加前缀(如 u、cfg)避免和业务其它模块撞名,定期用 removeStorageSync 清理过期数据,防止 10MB 配额被悄悄占满。

本文示例基于微信小程序基础库 2.x 与官方文档说明(官方文档)。

总结

小程序 页面传参没有唯一正确答案,三种做法按场景分工:URL 参数最直观,适合页面跳转带的少量一次性数据,拼在链接后面、onLoad 的 options 里接收;globalData 全局变量适合跨页面共享的进程级状态,通过 getApp() 读写、重启即失;本地缓存 Storage 是唯一能持久化的方式,适合大对象或需要跨会话保留的数据,记得先 JSON 序列化。一句话记忆:小数据走 URL,全局状态挂 globalData,大且持久用 Storage。页面传参的坑大多集中在编码、容量和持久边界上,按上面清单逐一验证就能避开。

延伸学习

常见问题

Q:小程序页面传参怎么传对象或数组?

URL query 只能传字符串,直接拼对象会被转成 [object Object]。正确做法是用 JSON.stringify 把对象转成字符串再拼进 URL,目标页用 JSON.parse 还原;如果对象较大或要持久,更推荐改用本地缓存 Storage,避免 URL 长度限制。

Q:globalData 和本地缓存 Storage 有什么区别?

globalData 存在内存里,小程序进程销毁后归零,读写零成本、适合共享状态;Storage 落在本机文件,重启仍在,但有容量上限且是明文。简单说,globalData 管「这次运行期间的共享」,Storage 管「下次打开还在的数据」。

Q:为什么 URL 参数里的中文到目标页变成乱码或丢失?

因为 URL 对特殊字符和中文字符有特殊处理,没编码就拼进去会被截断或错误解析。跳转前用 encodeURIComponent 对值编码,目标页 onLoad 里用 decodeURIComponent 解码即可修复,这是页面传参最常见的失败点。

0 人点赞