想象一下,你经营着一家开在手机里的“小程序餐厅”。餐厅本身是小程序,App 生命周期是餐厅的开业与打烊流程;每一张餐桌是一个“页面 Page”,一桌客人从入座、点菜到结账离开的全程,就是页面生命周期;而客人在大厅、包间、吧台之间走动的路线,就是页面路由。作为老板,你要决定何时开门备货、何时迎宾、客人换桌时怎么引导、后厨怎么传菜、食材怎么保鲜。这篇文章就用“管餐厅”的视角,带你把小程序生命周期与页面路由一次看透。

岗位关联

小程序生命周期与页面路由是移动端开发的地基工程。掌握它,你就能精准掌控资源释放时机、设计合理的页面跳转闭环,写出不掉帧、不泄漏、体验顺滑的小程序。

目录
  1. 可视化演示
  2. 应用生命周期:餐厅的开门与打烊
  3. 页面生命周期:一桌客人的用餐全程
  4. 组件生命周期:餐具的流转
  5. 路由跳转方式:客人的四种移动动线
  6. 页面栈管理:服务员的动线账本
  7. 参数传递机制:点菜单上的需求条
  8. 缓存与内存:食材保鲜与库存管理
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

演示小程序App/Page/Component生命周期回调与页面栈路由跳转机制。

本节配有交互动效(进入视口后自动加载,约 3KB)。

生命周期 App Page onLoad→onUnload 生命周期回调管理资源

应用生命周期:餐厅的开门与打烊

一家餐厅每天的开业节奏是固定的:开门前采购备料,门一开迎客,忙完一轮暂时歇口气,客人走了再重新迎接。小程序 App 的生命周期也是如此——它就是整个餐厅的营业时间表。onLaunch 是开门前的备料环节,小程序冷启动时只触发一次;onShow 是正式开门迎客,每次从后台回到前台都会触发;onHide 是挂上“暂停营业”的牌子;onError 则是后厨着火时的应急机制。

从技术角度讲,App 级生命周期回调在小程序的 app.js 中定义,是整个应用的“总开关”。onLaunch 适合做初始化工作,比如检查登录状态、拉取全局配置;onShow 适合恢复需要实时更新的数据;onHide 适合暂停正在播放的音频、关闭高频定位。它们的触发时机由微信/支付宝/抖音等平台统一调度,开发者只需在对应回调里填入合适的“工作清单”。

用一个时间轴可以更清楚地看出规律:应用从启动开始,经历“前台-后台-前台”的循环,直到用户退出小程序。注意 onShow 和 onHide 是成对出现的,而 onLaunch 一生只会出现一次。这个规律决定了全局资源的加载与释放时机。

开一家店,老板必须知道什么时候备料、什么时候开门、什么时候打烊。小程序全局的状态管理也是这样:把初始化放进 onLaunch,把恢复动作放进 onShow,把暂停动作放进 onHide,这台餐厅永远不会在错误的时间做错误的事。

tshow(1)  ≤  thide(1)  +  Δback  ≤  tshow(2)  =  tlaunch  +  Tuse  +  Δback\displaystyle t_{show}^{(1)} \;\leq\; t_{hide}^{(1)} \;+\; \Delta_{back} \;\leq\; t_{show}^{(2)} \;=\; t_{launch} \;+\; T_{use} \;+\; \Delta_{back}
应用每次从后台回到前台,都像餐厅重新掀开帘子接待客人,onShow 再次响起迎客铃。
  1. 首次前台时刻t_show^(1) 表示应用第一次出现在用户面前的时刻,对应 onShow 首次触发。
    tshow(1)t_{show}^{(1)}
  2. 退入后台时刻t_hide^(1) 是应用被切到后台的时刻,Δ_back 是后台停留时长,onHide 在此刻触发。
    thide(1)+Δbackt_{hide}^{(1)} + \Delta_{back}
  3. 再次前台时刻t_show^(2) 是用户重新回到小程序的时刻,整个循环等于“首屏使用时长 + 后台停留时长”。
    tshow(2)=tlaunch+Tuse+Δbackt_{show}^{(2)} = t_{launch} + T_{use} + \Delta_{back}

页面生命周期:一桌客人的用餐全程

餐厅的每张餐桌都有自己独立的“接待流程”:客人进门被引导入座,服务员递上菜单,后厨开始备菜,客人用完餐结账离开,服务员撤桌清扫。小程序里每个页面 Page 的生命周期,就是这张餐桌的完整服务流程。当 onLoad 触发时,相当于客人刚入座,服务员递上点菜单;onShow 是客人真正看到这桌的状态;onReady 是菜品和餐具全部就位;onHide 是客人暂时离席;onUnload 则是客人结账走人、撤桌清洁。

在代码层面,页面生命周期回调是每个 Page 的标配。首次进入页面时回调顺序固定为 onLoad → onShow → onReady。其中 onLoad 只执行一次,且能接收上一个页面通过路由带来的参数;onShow 每次页面变得可见都会触发;onReady 表示首帧渲染完成,可以安全操作组件。当页面被 navigateTo 压入后台时触发 onHide,被 navigateBack 或 redirectTo 移除时触发 onUnload。

理解这套顺序的价值在于掌握“资源分配”的时机。onLoad 适合拉取数据、初始化变量;onReady 适合获取节点信息、开始动画;onUnload 必须清理该页面专属的定时器和监听器,否则相当于客人走了,桌上的餐具和垃圾没人收拾,越积越多。

客人走后不撤桌,下一桌客人就没法入座。同理,页面被卸载时不清除定时器、不解绑监听,内存就会持续膨胀。把 onUnload 当成“撤桌流程”,每次必做清理,你的小程序就能长时间流畅运行。

Init=onLoad  →  onShow  →  onReady,Revisit=onShow  →  onHide\displaystyle Init = onLoad \;\rightarrow\; onShow \;\rightarrow\; onReady, \qquad Revisit = onShow \;\rightarrow\; onHide
初次来的客人走完整套流程,熟客再访只需打声招呼(onShow 到 onHide),不必重新入座。
  1. 初次拜访路径onLoad 负责接收参数并初始化,onShow 让页面可见,onReady 完成渲染。三者次序固定,不可颠倒。
    onLoad→onShow→onReadyonLoad \rightarrow onShow \rightarrow onReady
  2. 再次访问路径页面已在栈中时,重新显示只走 onShow,隐藏只走 onHide,不会再触发 onLoad。
    onShow→onHideonShow \rightarrow onHide

组件生命周期:餐具的流转

好的餐厅离不开餐具的高效流转。刀叉、盘子、杯子从仓库取出、摆上桌、被使用、最后撤下清洗,构成一个完整的循环。小程序的自定义组件 Component 也有类似的生命周期:created 相当于餐具从仓库取出(此时组件实例已创建但还没上桌);attached 相当于餐具被摆到餐桌上(组件挂载进页面节点树);ready 是餐具各就各位、可以正式使用;detached 是撤桌清洗(组件从节点树移除)。

技术细节上,组件生命周期建议写在 lifetimes 字段中。created 时组件还没有进入到页面结构里,所以不能调用 this.triggerEvent 之类依赖组件关系的接口,但可以初始化自身数据;attached 之后可以获取组件上下文;ready 之后可以安全操作组件内部的节点。除此之外还有 observers 字段用于监听属性变化——就像服务员时刻留意客人的水杯,一旦空了就主动加水。

组件生命周期与页面生命周期的关系是“嵌套”的:页面 onLoad 时内部的组件可能正在走 created → attached,页面 onReady 时组件的 ready 也通常已结束。开发者在小程序工具中打开生命周期跟踪面板,可以直观看到这种“先父后子、子先就绪”的触发层次。

餐具流转不清洗会滋生细菌,组件销毁不清理会泄漏内存。把 detached 当作“撤桌清洗”的固定流程,组件就能在页面任何角落安全反复地使用。

created  →  attached  →  ready  →  detached\displaystyle created \;\rightarrow\; attached \;\rightarrow\; ready \;\rightarrow\; detached
餐具从出库到上桌再到清洗,严格按顺序流转,组件生命周期就是这套秩序的代码化身。
  1. 创建环节created 表示组件实例刚被构造,适合初始化内部数据,但此时组件还未进入页面节点树。
    createdcreated
  2. 挂载环节attached 表示组件已挂载到页面树中,可以访问组件外部传入的属性与插槽内容。
    attachedattached
  3. 就绪环节ready 表示组件渲染完成,可安全操作组件内节点,对应“餐具各就各位”。
    readyready
  4. 移除环节detached 表示组件从节点树移除,必须在此清理组件内的监听器和定时器。
    detacheddetached

路由跳转方式:客人的四种移动动线

客人在餐厅里的移动方式不止一种:从大厅走到包间,服务员会记住他的来路方便带他回来;从包间直接换到另一间包间,原包间立即被回收;吃完原路返回;或者通过固定通道去吧台取饮料。小程序页面路由的四种 API 恰好对应这四种动线。navigateTo 是“走进包间”,新页面压入页面栈,当前页保留在栈中并隐藏;redirectTo 是“换桌”,当前页被替换出栈并卸载;navigateBack 是“原路返回”,弹出栈顶页面;switchTab 是“走通道去吧台”,只能跳转到 tabBar 页面且不能携带参数。

代码层面,这些 API 都是全局方法:wx.navigateTo、wx.redirectTo、wx.navigateBack、wx.switchTab,以及“清场重来”的 wx.reLaunch。用栈的视角看,navigateTo 就是 push,navigateBack 就是 pop,redirectTo 是“弹出栈顶再压入新页”,switchTab 则特殊地清空非 tabBar 页面。选择哪种方式取决于业务语义:需要返回上一页就用 navigateTo,完成操作后不希望用户回到旧页面就用 redirectTo。

一个常见的业务场景是登录流程:用户从首页进入登录页,登录成功后应该用 redirectTo 跳回首页或业务页,而不是 navigateTo,否则用户按返回键又会回到登录页,就像换桌后服务员还带客人回已经撤掉的旧桌。

动线设计决定客人体验。支付成功后用 redirectTo 防止回退到收银台,详情页跳转用 navigateTo 确保用户可以逐级返回。路由 API 选对了,用户在小程序里的每一步都走得顺理成章。

Stack→  navigateTo  Stack∪{Pnew},Stack→  redirectTo  (Stack∖{Ptop})∪{Pnew}\displaystyle Stack \xrightarrow{\;navigateTo\;} Stack \cup \{P_{new}\}, \qquad Stack \xrightarrow{\;redirectTo\;} \big(Stack \setminus \{P_{top}\}\big) \cup \{P_{new}\}
navigateTo 和 redirectTo 的差别,就是“记住来路带客人回来”与“直接换桌回收旧桌”的差别。
  1. 入栈操作navigateTo 将新页面 P_new 追加到页面栈末尾,原栈保持不变,当前页被隐藏。
    Stack∪{Pnew}Stack \cup \{P_{new}\}
  2. 替换操作redirectTo 先从栈中移除栈顶页面 P_top,再将新页面压入,实现“换桌不回头”。
    (Stack∖{Ptop})∪{Pnew}(Stack \setminus \{P_{top}\}) \cup \{P_{new}\}

页面栈管理:服务员的动线账本

服务员手里有一本动线账本,每次带客人去新包间就记一笔,客人离开就划掉一笔,账本的深度有限。小程序正是用“页面栈”来管理所有页面的。页面栈是一个后进先出的结构:navigateTo 入栈,navigateBack 出栈,redirectTo 替换栈顶。平台对栈深度有硬性限制——最多 10 层,超过后会跳转失败,就像账本最多只能记 10 条动线,写满了就不能再带客人去新包间。

开发者可以通过 getCurrentPages() 获取当前页面栈的数组,最后一个元素就是当前页面实例。利用这个 API 可以做两件很实用的事:一是拿到上一页实例并调用它的方法更新数据,二是打印当前栈结构排查路由异常。页面栈的进出规则决定了 onShow 与 onUnload 的触发顺序——入栈时新页面 onLoad 后 onShow,旧页面 onHide;出栈时栈顶页面 onUnload,新栈顶页面 onShow。

栈深度限制是新手最容易踩的坑。如果页面反复用 navigateTo 跳转而不及时 navigateBack,到第 11 次跳转就会失败。所以在设计“详情页 → 评论页 → 举报页”这类深层链路时,务必评估某些中间页是否应用 redirectTo 替换掉,主动控制账本厚度。

动线账本记得太多,服务员会手忙脚乱。小程序同样如此——定期用 getCurrentPages() 检查栈深度,用 redirectTo 代替无意义的 navigateTo 链路,你的页面导航就不会“爆栈”。

Depth(Stack)=∑p∈Stack1  ≤  10\displaystyle Depth(Stack) = \sum_{p \in Stack} 1 \;\leq\; 10
账本最多记十页,页面栈同样最多十层——超出的动线会被服务员礼貌拒绝。
  1. 统计栈深对页面栈 Stack 中的每个页面累加计数,得到当前栈深度。
    ∑p∈Stack1\sum_{p \in Stack} 1
  2. 深度上限平台规定栈深不超过 10,Depth(Stack) ≤ 10 是合法状态,超过则跳转失败。
    ≤10\leq 10

参数传递机制:点菜单上的需求条

客人想吃什么,写在点菜单上递给后厨;客人有什么忌口,记录在餐厅的熟客档案里;服务员之间需要实时沟通,直接通过对讲机喊话。小程序页面之间的数据传递同样有三种主要通道。第一种是 query 参数:跳转时把数据拼在 URL 后面,目标页在 onLoad(options) 中接收,就像点菜单上写明“少辣、不要香菜”。第二种是全局数据 app.globalData:挂载在 App 实例上的公共对象,适合存放登录令牌、用户信息,相当于餐厅的公用档案。第三种是本地缓存 wx.setStorageSync:把数据持久化到设备存储中,适合存放草稿、历史记录,相当于熟客档案柜。

实战中 parameter 传递的细节容易被忽略:navigateTo 的 URL 长度有限且需要编码特殊字符,对象类型必须先 JSON.stringify 再传;switchTab 不能带参数,此时应借助全局变量或缓存;EventChannel 可以在两个页面间建立双通道通信,类似给服务员配备对讲机,navigateTo 成功回调里通过 eventChannel.emit 传输数据,目标页在 onLoad 里 getOpenerEventChannel().on 监听接收。

选择哪种通道没有绝对标准,核心是数据和生命周期要匹配:临时一次性数据走 query,会话级数据走 globalData,需要跨冷启动保留的数据走缓存,父子页面间的即时回传走 EventChannel。

传参方式没有银弹,只有最合适的场景。把一次性需求写在“点菜单”上,把长期信息放进“档案柜”,把临时协作丢给“对讲机”,你的页面间数据流就会像餐厅后厨一样井然有序。

Θpage=Decode(URLencode(query)),Θglobal=app.globalData[key]\displaystyle \Theta_{page} = Decode\big(URL_{encode}(query)\big), \qquad \Theta_{global} = app.globalData[key]
点菜单上的字要写清楚、归档的档案要放对柜子,数据传到哪、放多久,全看通道的性质。
  1. 页面参数解码query 参数在 URL 中编码传输,目标页拿到后通过 Decode 还原成原始数据。
    Decode(URLencode(query))Decode(URL_{encode}(query))
  2. 全局数据读取app.globalData[key] 直接从全局对象中按键取值,不受页面栈变化的干扰。
    app.globalData[key]app.globalData[key]

缓存与内存:食材保鲜与库存管理

再大的餐厅也有冰柜容量上限;再稳的小程序也有存储限制。本地缓存就像餐厅的冰柜——里面可以存熟客口味、半成品食材等,但冰柜总共只有 10MB 空间,超过上限后写入失败。缓存 API 分为同步与异步两套:wx.setStorageSync / wx.getStorageSync 是“吧台即时存取”,读写快但会阻塞 JS 线程;wx.setStorage / wx.getStorage 是“后仓批量存取”,不阻塞主线程,适合大体积数据。除了总量上限,还要注意缓存数据在用户清理微信缓存时会丢失,所以不能把它当作永久数据库。

与缓存同等重要的是内存管理。页面的 onUnload 必须清理定时器和监听器,否则“撤桌不洗碗”会引发内存泄漏;onHide 时应该暂停视频和音频播放,减少后台占用;图片资源尽量走 CDN 懒加载,避免一次加载过多大图。组件销毁时同样要清理 observers 之外的动态监听。内存清理的黄金法则是:谁创建,谁释放。

实际开发中,缓存策略和生命周期的配合非常关键:在 onLoad 中先读缓存展示旧数据,再发请求更新数据并写回缓存——这是“缓存优先、网络兜底”的经典模式;在 onUnload 中把用户编辑的草稿写入缓存,防止误退出丢数据。这样既保证了首屏速度,又照顾了数据新鲜度。

把缓存当成冰柜、把内存当成后厨空间,定期“清库存”是经验丰富的老板的必修课。定时器清掉、监听器解绑、大图懒加载、缓存限量使用,这套组合拳打下来,小程序就能像高效运转的餐厅一样长期不卡顿。

∑k∈Storagesize(k)  ≤  10 MB,Memlive=∑obj∈Heapretain(obj)\displaystyle \sum_{k \in Storage} size(k) \;\leq\; 10\,MB, \qquad Mem_{live} = \sum_{obj \in Heap} retain(obj)
冰柜满了就放不下新食材,内存泄漏就等于冰柜一直开着门——该冻住的冻不住,该拿的拿不出来。
  1. 存储总量约束Storage 中所有键 k 的大小之和,不能超过 10MB 的平台额定配额。
    ∑k∈Storagesize(k)≤10 MB\sum_{k \in Storage} size(k) \leq 10\,MB
  2. 存活对象统计Mem_live 表示堆内存中仍被引用的对象总量,引用链越长越难释放,小程序越卡。
    ∑obj∈Heapretain(obj)\sum_{obj \in Heap} retain(obj)

AI 应用场景

页面停留时长埋点分析

阶段:数据采集与处理

解决的问题:传统做法是手动在各页面 onShow、onHide 里写重复的统计代码,代码冗余且容易漏掉页面。利用生命周期钩子统一封装埋点 SDK,可在 onShow 记录进入时间、onHide 计算停留时长,为 AI 用户画像模型提供高质量行为数据。

具体案例:在电商小程序的 Page.onShow 中调用埋点 SDK 上报 page_start,在 onHide 上报 page_end 并附带时长。后端清洗后喂给推荐模型,用于预测用户下一单可能进入的商品页。

路由拦截与登录态校验

阶段:模型推理与业务集成

解决的问题:未登录用户直接访问订单页会触发接口 401,体验很差。AI 助手型的业务页更需要确保用户处于有效会话。通过在页面 onLoad 中统一读取 globalData.token,无令牌则 redirectTo 到登录页,登录成功再 navigateBack,形成闭环。

具体案例:某 AI 绘画小程序中,用户点击“我的作品”时页面 onLoad 检查本地缓存的登录态,未登录则跳转授权页,同时携带 redirect_url 参数,登录后自动回到原页面并带着用户 token 刷新作品列表。

本地缓存驱动的 AI 会话草稿

阶段:应用开发与部署

解决的问题:AI 对话页面中,用户编辑的提示词(prompt)如果不缓存,切出小程序后就会丢失。利用 onHide 自动将草稿写入 wx.setStorageSync,在 onLoad 恢复,实现会话的“无感续聊”。

具体案例:AI 写作助手的提问页,用户在页面中输入长文本 prompt,中途切到微信聊天再回来时,onShow 立即从缓存恢复草稿内容;用户输入完成后点击生成,AI 请求发出后若页面被回收,onUnload 也会兜底保存,确保内容不丢。

Python 演示

python71 行
class Page:    def __init__(self, name):        self.name = name     def on_load(self, params):        print("[Page:%s] onLoad, params=%s" % (self.name, params))     def on_show(self):        print("[Page:%s] onShow" % self.name)     def on_ready(self):        print("[Page:%s] onReady" % self.name)     def on_hide(self):        print("[Page:%s] onHide" % self.name)     def on_unload(self):        print("[Page:%s] onUnload" % self.name)  class MiniApp:    MAX_DEPTH = 10     def __init__(self):        self.stack = []        self.global_data = {}     def launch(self):        print("[App] onLaunch")        self.show()     def show(self):        print("[App] onShow")     def hide(self):        print("[App] onHide")     def navigate_to(self, page, params=None):        if len(self.stack) >= self.MAX_DEPTH:            raise RuntimeError("page stack is full")        if self.stack:            self.stack[-1].on_hide()        self.stack.append(page)        page.on_load(params or {})        page.on_show()        page.on_ready()     def redirect_to(self, page, params=None):        self.stack.pop().on_unload()        self.stack.append(page)        page.on_load(params or {})        page.on_show()        page.on_ready()     def back(self, delta=1):        for _ in range(delta):            self.stack.pop().on_unload()        if self.stack:            self.stack[-1].on_show()  if __name__ == "__main__":    app = MiniApp()    app.launch()    home = Page("home")    app.navigate_to(home)    detail = Page("detail")    app.navigate_to(detail, {"id": 1001})    app.back()    edit = Page("edit")    app.redirect_to(edit)
目的:模拟小程序页面栈的入栈、出栈、替换操作,并打印对应的生命周期回调顺序。
  1. class Page:定义页面模型,每个页面拥有 onLoad、onShow、onReady、onHide、onUnload 五个生命周期方法,对应正文中的“餐桌服务流程”。
  2. def on_load(self, params):模拟页面接收路由参数,对应实际小程序中 onLoad(options) 接收 query 参数。
  3. self.stack = []用列表模拟页面栈,栈顶永远是列表末尾元素,对应“服务员的动线账本”。
  4. if len(self.stack) >= self.MAX_DEPTH:校验栈深度是否超过 10 层,超过则抛出异常,对应平台的栈深限制。
  5. self.stack[-1].on_hide()压入新页面前,先将当前栈顶页面隐藏,符合 navigateTo 的时序。
  6. self.stack.append(page)新页面入栈,相当于服务员在账本上新记一条动线。
  7. self.stack.pop().on_unload()弹出栈顶页面并触发其 onUnload,对应 navigateBack 的撤桌流程。
  8. def back(self, delta=1):按指定步数出栈,出栈后让新栈顶页面触发 onShow,对应“原路返回”。

常见误区

误区我从列表页点进详情页,编辑完返回再点另一篇详情,详情页的 onLoad 应该每次都执行吧?不然数据怎么刷新的?

正解onLoad 只在页面实例首次创建时执行一次,相当于客人第一次入座才递菜单。当页面通过 navigateBack 从栈中恢复时,触发的是 onShow 而不是 onLoad。要在每次进入都刷新数据,应该把拉取逻辑放在 onShow 中,因为它每次可见都会触发。这和“熟客再访只需打招呼,不用重新点菜”是同一个道理。

为何常见:新手往往把“页面每次出现”和“页面重新创建”混为一谈,以为回到页面就等于重新加载,忽略了页面栈的复用机制。

误区小程序页面随便跳,反正导航栏有返回按钮,用户总能原路返回。

正解页面栈深度上限是 10 层,超出后 navigateTo 直接失败。这就像服务员的动线账本最多记 10 页,写满就不能再带客人去新包间了。电商“首页→搜索结果→商品详情→店铺→…”这类深层链路很容易触顶,需要及时用 redirectTo 替换中间页,或用 navigateBack 回退。

为何常见:很多开发者只关注“跳转能不能成功”,却不关注“返回链路有没有闭环”,等到线上出现跳转静默失败才排查出栈深问题。

误区我把数据存到 `wx.setStorageSync` 里,这不就是本地数据库吗?用户随时都能读出来。

正解缓存像冰柜,虽然能保鲜,但冰柜容量只有 10MB,且用户主动“清理微信存储”时缓存会被清空,小程序版本升级也可能导致部分缓存失效。缓存应放可重建的半成品数据(如草稿、用户设置),绝不可放唯一重要的数据。付费记录、账号凭证等核心信息应存服务端,本地只做加速。

为何常见:开发者习惯把缓存当作数据库使用,混淆了“临时存储”与“持久化存储”的边界,直到用户清缓存后数据全丢才意识到问题。

误区我在 onUnload 里清了定时器,所以页面资源肯定能释放。

正解onUnload 在正常 navigateBack 出栈时确实会触发,但小程序在某些极端场景(如内存告警被系统直接回收)下不会走正常的 onUnload 流程。因此重要的清理逻辑不能只在 onUnload 做,还要在 onHide 里做好兜底——前台的定时器暂停、视频停止播放等操作应双保险。这就像餐厅不仅要靠跑单结算,还要在打烊前定期盘点后厨。

为何常见:开发者把 onUnload 当作万能清理钩子,忽略了移动端系统“强杀进程”的现实——系统可不会等你优雅地洗完碗再断电。

面试题

Q1. 小程序应用级生命周期中,只在冷启动时触发一次的回调是哪个?

单选题 · easy

  • A.onShow
  • B.onLaunch← 正确答案
  • C.onHide
  • D.onError

答案:B。onLaunch 对应餐厅开门前的备料,整个营业日只做一次;onShow 每次回前台都触发;onHide 每次进后台触发;onError 在出错时触发。备料只做一次,迎客每次都要。

Q2. 页面首次加载时,生命周期回调的正确顺序是?

单选题 · easy

  • A.onShow → onLoad → onReady
  • B.onLoad → onShow → onReady← 正确答案
  • C.onLoad → onReady → onShow
  • D.onReady → onShow → onLoad

答案:B。初始顺序为 onLoad → onShow → onReady,对应“入座 → 看到餐桌 → 服务就绪”。其他选项顺序颠倒,违反了页面初始化的固定时序。

Q3. 使用 wx.navigateTo 从首页跳转到列表页后,首页的生命周期状态是?

单选题 · mid

  • A.触发 onUnload,从页面栈移除
  • B.触发 onHide,保留在页面栈中← 正确答案
  • C.触发 onShow,继续在前台运行
  • D.不触发任何回调,原样保留

答案:B。navigateTo 是“走进包间”,原页面像客人暂时离开旧桌一样触发 onHide,但桌子还留着(页面栈保留),返回时直接 onShow 恢复。A 是 redirectTo 的表现;C 错误,新页面在前台;D 错误,隐藏必触发 onHide。

Q4. 小程序页面栈的最大深度是多少层?

单选题 · mid

  • A.5 层
  • B.10 层← 正确答案
  • C.15 层
  • D.20 层

答案:B。平台规定页面栈最多 10 层,超过 10 层后 navigateTo 会失败。这就像动线账本最多记录 10 条动线,写满后拒绝再带客人去新包间,必须先返回再跳转。

Q5. 下列哪些操作会导致当前页面触发 onUnload?

多选题 · mid

  • A.调用 wx.redirectTo 替换当前页← 正确答案
  • B.调用 wx.navigateBack 返回上一页← 正确答案
  • C.调用 wx.navigateTo 跳转新页面
  • D.调用 wx.switchTab 切换 tab

答案:AB。redirectTo 将当前页弹出栈并卸载,navigateBack 将栈顶弹出并卸载,二者都会触发 onUnload。navigateTo 只是隐藏当前页,switchTab 会关闭非 tabBar 页面但保留 tabBar 页面本身,若当前页是 tabBar 页则不卸载。

Q6. 在页面 anyPage 中调用 wx.navigateBack({ delta: 2 }),下列说法正确的是?

单选题 · hard

  • A.一定会返回到栈中倒数第三个页面
  • B.若栈深为 2,则 back 失败且无任何回调
  • C.出栈的两个页面都会触发 onUnload← 正确答案
  • D.新栈顶页面会触发 onReady

答案:C。navigateBack 弹出 delta 个页面,每个被弹出的页面触发 onUnload;若 delta 超过栈深则回退到栈底页。B 错在“无任何回调”——返回后会触发新栈顶 onShow;A 超界时不一定到达倒数第三页;D 错,新栈顶触发的是 onShow 而非 onReady。

Q7. 关于自定义组件生命周期,下列说法正确的是?

单选题 · hard

  • A.created 阶段可以调用 triggerEvent 触发事件
  • B.attached 阶段组件已挂载到页面节点树← 正确答案
  • C.detached 阶段组件仍可正常操作节点
  • D.ready 阶段组件尚未渲染完成

答案:B。attached 表示组件已挂载到节点树,可以访问外部属性与插槽,相当于餐具已经摆上桌。created 时组件未挂载,不能触发事件;detached 时组件已被移除,不能操作;ready 时渲染已完成,D 说法相反。

Q8. 要在页面 B 中接收页面 A 通过 wx.navigateTo 传递的参数,应该在哪个回调中读取?

单选题 · mid

  • A.onShow(options)
  • B.onLoad(options)← 正确答案
  • C.onReady(options)
  • D.onHide(options)

答案:B。onLoad 的生命周期回调会收到路由参数 options,对应“客人入座时服务员递上写好的点菜单”。onShow 和 onReady 不接收参数,onHide 在页面隐藏时触发故无法收到新参数。

Q9. 下列哪些做法可以有效降低小程序的内存压力?

多选题 · hard

  • A.在 onUnload 中清除页面定时器← 正确答案
  • B.在 onHide 中暂停视频播放← 正确答案
  • C.将大对象长期挂在 globalData 上
  • D.图片使用 CDN 懒加载← 正确答案

答案:ABD。清除定时器、暂停视频、图片懒加载分别对应“撤桌洗碗、离店关灯、按需进货”,都能降低常驻内存。而将大对象长期挂在 globalData 上等于把不用的食材一直霸占冰柜,阻碍内存回收,属于反面做法。

Q10. 关于 wx.switchTab,下列描述正确的有?

多选题 · easy

  • A.只能跳转到 tabBar 页面← 正确答案
  • B.会关闭所有非 tabBar 页面← 正确答案
  • C.会销毁所有 tabBar 页面实例
  • D.跳转时不能携带 query 参数← 正确答案

答案:ABD。switchTab 是“走固定通道去吧台”,只能去 tabBar 页面,同时关闭其他非 tabBar 页面,并且无法携带参数。tabBar 页面实例会保留而非销毁,所以 C 错误。设计 tabBar 切换时要把共享数据放缓存或全局变量,而不是依赖 URL 传参。