2026年了,Uni-app 打的 App 在安卓上杀掉进程再打开就要重新登录,微信小程序里却还认得我,这两边的缓存不是同一个地方吗?
结论先说:Uni-app 打的 App 在安卓上杀掉进程再打开要求重新登录,微信小程序里却还很自然地认得你,多数情况不是 uni.setStorage 没写进去,而是两端的存储容器、启动恢复顺序和 token 失效策略没对齐——小程序缓存由微信客户端托管,App 端落到原生存储,本来就是两份不同的数据。2026 年按项目交付经验,先把「同一键名、同一序列化、同一恢复入口」这三件事对齐,再决定是补一层封装还是改续期,通常比让用户重新登录更省返工。
小程序里还认得我,App 冷启动却像第一次打开,差在哪
差在两件事:谁在管这份缓存,以及什么时候会被清掉。小程序端运行在微信容器里,缓存由微信客户端托管,用户不主动清理、不退出登录,数据往往能留很久;App 端落到系统原生存储,受系统存储管理、用户清缓存、清理软件的清理策略影响,被杀进程后能不能读回来,取决于写入路径和恢复入口是否集中。同一句 uni.setStorage,在不同端背后走的不是同一份东西,所以「开发者工具里明明还在」这种观察,很难直接推到真机冷启动上。
还有一个更隐蔽的差异:热启动不等于冷启动。用热重载、切后台再切回来测试,内存里的状态还在,看起来一切正常;只有真正杀进程再打开,才会暴露出恢复顺序的问题。2026 年做多端项目时,验收口径里建议明确写一条:至少在一台安卓真机上做一次杀进程冷启动验证。
- 误判一:只在 H5 或小程序里验证过,没在 App 真机冷启动验证。
- 误判二:登录页 onLoad 先写了一个空的用户对象,把已有登录态覆盖掉了。
- 误判三:token 存了、用户信息没存,而启动判断条件用的是用户信息。
- 误判四:页面上用内存变量当「已登录」标记,冷启动必然丢。
先别急着改存储,先分清是没存住还是没恢复
排查顺序建议从「读得到吗」开始,而不是从「写进去了吗」开始。在 App.vue 的 onLaunch 里打印一次 getStorageSync 的返回值和类型,再和写入处的键名、数据结构对一遍,多数情况下十分钟内就能判断方向,避免在错误的地方反复改代码。
- 记录返回值:在 onLaunch 读取 token 与用户信息,记下返回的是空字符串、null,还是结构不对的对象。
- 核对键名:检查写入处是否换了 key,或者一边存对象、一边存 JSON.stringify 后的字符串。
- 检查覆盖:确认启动阶段有没有默认值写入、退出登录逻辑有没有被误触发。
- 真机复测:杀进程后冷启动验证,不用热重载代替冷启动。
这一步的合格标准很朴素:同一份登录数据,在小程序、App、H5 三端读出来的字段名和类型一致。做不到这一点,后面所有失效处理都会退化成打补丁。
存储、恢复、失效三层核对,别混在一起查
把问题拆成三层,是因为三层验收口径不同:存储层看键名和类型是否一致,恢复层看启动顺序会不会覆盖,失效层看 401 之后是否只跳一次登录。三层混查,就容易陷入「改了存储还是丢」的循环。
- 存储层:统一 key 前缀、统一序列化方式,写入后立即读一次自校验;合格标准是三端读出的类型一致。
- 恢复层:只在 onLaunch 恢复一次,登录页不写默认空值;合格标准是冷启动后首屏就能拿到 token。
- 失效层:401 统一拦截,刷新 token 只允许一个请求在跑;合格标准是并发 401 只跳一次登录页。
一个交付现场的例子:2026 年有个项目要求小程序和安卓 App 两周内一起提测,接口方还没提供刷新 token 的字段,而登录页在 onLoad 里默认写入了一个空用户对象。当时能做的选择不多,做法是先把 key 收成带前缀的常量、恢复只在 App.vue 的 onLaunch 做一次、登录页去掉默认写入,401 先在请求封装里做一个兜底(清 token 并跳一次登录页)。结果是冷启动掉登录的反馈基本消失;代价也有——首版漏了 nvue 页面走的是另一条读取路径,提测后返工大约半天到一天(经验区间,取决于页面数量),并发 401 仍会跳两次登录,只能留到下一个迭代补请求队列。这类返工常见并不难在技术上,而是恢复入口太散。
- 用内存变量做「已登录」标记,冷启动必丢。
- 在每个页面的 onShow 里重复恢复,状态互相覆盖。
- 刷新 token 没有队列,多个 401 同时把用户踢出去。
- 把「未登录」和「token 已过期」当成同一种跳转,提示语也跟着混。
直接读 storage、加一层持久化封装、后端续期,三种做法各自适合谁
三种做法没有绝对优劣,关键看团队规模和登录态的复杂度。2026 年比较常见的路径是:先用一层轻量封装把读写入口收拢,再根据 token 有效期决定要不要补后端续期,避免一上来就上重架构。
- 直接读 storage:改动小,常见在 0.5 人天以内;适合页面少、token 有效期长的活动型项目;风险是失效判断散落在各页面,后续加需求容易漏。
- 加一层持久化封装:常见区间约 1 到 2 人天;适合多端共用、需要统一 key 与恢复入口的项目;风险是过度封装,简单项目反而多绕一圈。
- 后端续期加前端统一拦截:联调周期常见 2 到 5 天(经验区间,取决于接口就绪程度);适合 token 短效、并发请求较多的业务;风险是接口没就绪时前端只能先做占位。
判断做得好不好,看的不是封装了几层,而是三个可复测的结果:冷启动首屏是否带登录态、并发 401 是否只跳一次登录、退出登录是否把相关端的数据清干净。三项都能复测通过,才算收口。
什么情况值得这么做,什么情况别上
适合的场景:多端同时上线、需要冷启动恢复登录态、token 有明确有效期、团队已经有统一请求封装。这类项目把 key、序列化和恢复入口收拢,收益通常大于成本。
不适用或需要谨慎的场景:一次性活动页、纯展示型 H5、登录态只存在当前会话且用户能接受重新登录的内部后台工具——给这类项目上完整持久化层,维护成本可能高于收益。另外,安全要求较高的业务不宜只依赖前端 storage 保存敏感信息,需要按平台规范和后端方案另行核对。边界可以单独记一句:登录态问题先分层,再决定是否封装,不能把所有的「掉登录」都算成缓存没存住。
常见问题
Uni-app 里 uni.setStorageSync 存了 token,App 冷启动读出来是空字符串,先查哪一步?
先核对写入与读取的 key 是否完全一致、存的是对象还是字符串,再看 onLaunch 之前有没有默认值把已有登录态覆盖,最后在真机上杀进程复测一次。
小程序里能读到缓存,为什么 App 端读不到同一份?
两端并不是同一个存储容器:小程序缓存由微信客户端托管,App 落到原生存储。键名、序列化方式不统一时,App 端就可能读成空值,需要在同一个封装里读写。
登录态一丢就让用户重新登录,是不是更省事?
短期省事,但会把冷启动失败、token 过期和并发 401 混成同一种体验。建议至少区分「未登录」和「已过期」,跳转位置和提示文案分别处理。
App 端要不要把 token 改存到 plus.storage,而不是用 uni 的接口?
业务层统一用 uni 接口通常够用。确有 nvue 或原生层读取需求时再评估 plus 存储,但要同步清理策略,避免两份数据不一致。
恢复登录态放在 App.vue 里做一次,还是每页 onShow 都判断?
集中一次更稳。每页重复恢复容易出现状态互相覆盖,页面上只做「没有登录态就跳登录页」的轻判断即可。
下一步建议:先在 App.vue 的 onLaunch 里加一次读取日志,确认冷启动时 token 到底在不在;再把 key、序列化和 401 跳转收进同一个请求封装文件。这套做法适合多端共用登录态的项目,不适合可接受每次重登的一次性页面;涉及敏感信息时,需按平台规范与后端方案另行核对。
-
2026年了,弹窗里点“获取验证码”,整张表单也跟着提交了,是按钮默认 type 在作怪吗?
日期:2026年10月30日 阅读:16
-
2026年了,AI 把官网首页出得挺快,目标机型上一开就图片错位、按钮点不到,兼容和性能这摊还该前端收吗?
日期:2026年10月29日 阅读:56
-
2026年了,述职被问前端体验架构师和普通前端差在哪,我拿什么证明才算数?
日期:2026年10月28日 阅读:34
-
2026年了,活动页倒计时锁屏几分钟再打开就慢了一大截,用户截图问是不是我们少给了时间,问题出在哪?
日期:2026年10月27日 阅读:92
-
小屏安卓机上 Flutter 登录页键盘一弹,输入框就钻到键盘底下了,只能整页改成滚动吗?
日期:2026年10月25日 阅读:77




