小程序跨端开发

共 22 题
📑 题目列表 22 题
#
★★★

1. 小程序双线程架构(逻辑层/渲染层)与通信瓶颈本质

小程序采用逻辑层与渲染层分离的双线程架构,其设计动机是什么?该架构下的通信瓶颈本质是什么,对开发有什么影响?

  • 双线程架构(逻辑层 JS 引擎 / 渲染层 WebView)与安全隔离动机
  • 层间通信走 native 桥接与 setData 全量数据传递的瓶颈
  • 架构对 API 能力与代码写法的约束

小程序把逻辑层(JSCore/V8 运行 JS,处理业务逻辑与数据)与渲染层(WebView 渲染 WXML/WXSS)分开,中间通过 native 层桥接通信,核心动机是安全隔离与性能管控:逻辑层无法直接操作 DOM,只能通过 setData 提交数据,平台可对数据大小、频率做限制,防止恶意代码与劣质代码拖垮渲染;同时逻辑层独立于 WebView,JS 执行不被页面渲染阻塞。代价是每次数据更新都要走"逻辑层 JS → native 桥 → 渲染层"的序列化传输。

通信瓶颈的本质是:setData 会把数据从逻辑层 JS 对象序列化后经 native 桥全量(或按路径)传输到渲染层,渲染层再 diff 后更新视图,传输是跨线程/跨进程的异步消息,存在序列化开销与消息往返延迟;数据越大、频率越高,瓶颈越明显,尤其长列表与高频更新场景会出现明显卡顿。影响:代码必须遵循"小数据量、低频率"的 setData 原则(路径更新、合并多次为一次、避免传大对象),渲染层无 DOM API 意味着组件实现与 Web 习惯不同,长列表需用 recycle-view 等原生回收方案。

先答动机(隔离与管控)再答代价(桥接通信),把瓶颈落到"序列化 + 跨层消息"两个开销来源,最后落到 setData 使用约束,即构成完整逻辑链。理解"双线程不是性能优化,而是安全与管控的产物"是关键。

#
★★★

2. 小程序运行时与 setData 的底层原理及性能优化要点

小程序 setData 的底层实现原理是什么?它为什么会慢?有哪些工程上可落地的性能优化要点?

  • setData 的数据传输链路(逻辑层序列化 → native → 渲染层 diff)
  • setData 慢的原因(全量数据、高频调用、大数据对象)
  • 优化要点:路径更新、合并调用、数据裁剪、局部渲染

setData 的底层链路是:逻辑层把传入的对象序列化为 JSON,经 native 桥发送给渲染层 WebView,渲染层解析后与当前 data 合并,再 diff 出变化节点更新视图。其中序列化、跨层消息传递与渲染层 diff 三段都有开销,且 setData 本身是异步的(传入的回调在渲染完成后触发)。它"慢"的本质:一是每次调用都产生跨线程消息与序列化开销,二是传入对象越大序列化越久,三是高频调用会排队导致渲染层积压。

优化要点:一是路径更新——setData 只更新变化字段(如 setData({ 'list[3].name': v })),避免整对象提交;二是合并与节流——把多次数据变更合并成一次 setData(如循环中收集变更后一次性提交),高频场景(滚动、输入)用节流/防抖;三是数据裁剪——提交前剔除与渲染无关的字段(如临时计算变量、冗余字段),大数据用分页或仅传可见区域数据;四是借助 WXS 处理高频变化的样式/文本,减少 setData 频率;五是长列表用 recycle-view,避免一次性渲染海量节点。核心原则:减少调用次数、减小单次数据量、缩小更新范围。

答题顺序:链路原理(三段开销)→ 慢的原因(量、频、范围)→ 优化手段(路径、合并、裁剪、WXS、回收列表)。能解释"为什么路径更新有效"(减少序列化与 diff 范围)即体现原理级理解,而非罗列技巧。

#
★★★

3. 小程序自定义组件与基础组件的差异及组件间通信方式

小程序自定义组件与基础组件有何差异?自定义组件之间、组件与页面之间的通信有哪些方式?

  • 自定义组件的封装、样式隔离与生命周期差异
  • 通信方式:properties、triggerEvent、selectComponent、全局事件
  • 组件间通信(兄弟/跨层)的工程方案

基础组件(view、text、button 等)是平台内置、直接渲染到原生/WebView 的原子组件,性能最优但不可扩展;自定义组件由开发者用 JSON/WXML/WXSS/JS 四个文件定义,可组合基础组件与业务逻辑、暴露 properties 对外接口,支持 styleIsolation 样式隔离(组件样式不影响外部、外部样式默认不影响组件内部)与独立的生命周期(attached/ready/detached 等),用于复用与拆分复杂 UI。差异核心:基础组件是渲染原子,自定义组件是业务封装单元。

通信方式:父传子用 properties(数据从父流向子,子修改需通过事件回传);子传父用 triggerEvent 触发自定义事件(父组件 bind:xxx 监听);父子间双向同步可结合 properties + triggerEvent 实现受控模式;页面与组件用 this.selectComponent('#id') 直接调用组件方法或获取 data;跨组件/跨页面通信用全局事件(如 EventChannel、全局 store、页面 getApp() 全局数据);组件间兄弟通信一般通过共同父组件中转或全局事件总线。选择原则:优先 properties + triggerEvent 的声明式链路,避免过多直接调用造成耦合。

先区分"渲染原子 vs 业务封装单元",再按"父传子/子传父/直接调用/全局"四类通信方式展开,最后给选择原则。能把 properties 单向流 + triggerEvent 回传的"受控"模式讲清楚是高分点。

#
★★★

4. Taro 3 的运行时适配原理(React 适配到小程序 DSL)

Taro 3 是如何在运行时把 React 组件适配到小程序 DSL 的?为什么它被称为"重运行时"架构?

  • Taro 3 的运行时架构:React 完整运行在逻辑层,渲染层用自定义组件模拟 DOM
  • 通过递归渲染把 React 元素树映射为小程序自定义组件树
  • 事件系统、样式与 DOM 模拟的实现要点

Taro 3 的核心思路是"把小程序当成一个浏览器/DOM 来模拟":编译时不做组件级代码转换(区别于 Taro 1/2 的静态编译),而是把 React/Vue 运行时原样打进小程序逻辑层,让 React 完整执行其虚拟 DOM 与 diff,再通过运行时适配层把虚拟 DOM 转换为对小程序 setData 的调用。渲染层则用一套内置的"模拟 DOM 组件"(如 view 对应 、text 对应 )搭出组件树,Taro 提供 document、createElement 等 DOM API 的兼容实现,使 React 的协调器(reconciler)以为自己在操作真实 DOM。

实现要点:一是自定义 reconciler 或 DOM 适配层,把 React 的 createInstance/appendChild 等操作翻译为小程序自定义组件与属性;二是事件系统,把 React 事件绑定到小程序组件并通过事件对象做冒泡模拟;三是样式处理,样式类名映射到小程序 class,动态样式转换为内联 style;四是更新合并,把一次渲染周期的多次 DOM 变更合并为一次 setData 提交。优点:React/Vue 生态(Hooks、Context、第三方库)几乎全兼容,心智一致;代价:逻辑层额外运行一套 DOM 模拟与 React 运行时,包体与内存开销增大,且受小程序 setData 瓶颈限制,性能敏感场景需配合 Taro 的渲染优化(如 memo、虚拟列表)。

理解"重运行时"的关键是:不转译框架代码,而是模拟 DOM 环境让框架原样运行。从"DOM 模拟层 + reconciler 翻译 + 事件/样式适配 + setData 合并"四部分讲实现,再讲收益与代价,即可覆盖本题。

#
★★★

5. Taro 如何将 React/Vue 组件编译为小程序 wxml/wxss/js

Taro 在编译阶段是如何把 React/Vue 组件转换为小程序 wxml/wxss/js 四件套的?哪些代码会被静态处理、哪些依赖运行时?

  • 模板编译:JSX/模板语法 → wxml 的静态转换
  • 样式编译:css → wxss,rpx 适配与选择器转换
  • 静态转换与运行时适配的分工边界

Taro 编译期对 React/Vue 源码做的是"模板与样式的静态转换 + 逻辑代码原样打包":JSX(React)或 template(Vue)被解析为 AST,再按小程序模板语法生成 wxml——JSX 中的元素标签映射为小程序组件名(div→view、span→text、button→button),表达式({this.state.x} 或 {{x}})转换为 wxml 插值,条件渲染(v-if/JSX &&)转换为 wx:if,列表渲染转换为 wx:for;样式文件经 postcss 处理生成 wxss,单位自动换算 rpx(默认 750 设计稿),不支持的选择器(如属性选择器、部分伪类)做降级或警告。业务 JS 不转换,作为"渲染无关的逻辑代码"打包进小程序 js 文件。

分工边界:凡是"在渲染层需要表达为模板结构"的部分(标签、属性、条件、循环、插值)尽量静态编译进 wxml,减少运行时开销;凡是"动态计算、事件回调、生命周期"的逻辑留在逻辑层 JS。Taro 3 中,模板只负责"搭骨架 + 插值 + 简单指令",复杂动态结构(如动态创建组件、render props 返回任意节点)无法静态编译,依赖运行时把动态内容渲染为"模板递归组件"(taro 内置的动态节点组件),这体现了"静态优先、运行时兜底"的设计:静态能覆盖的走编译,覆盖不到的由 DOM 模拟层在运行时用递归渲染补齐,两者配合保证框架兼容性。

答题主线是"静态转换什么、动态留什么":模板/样式走编译,逻辑走打包,再讲 Taro 3 的动态节点运行时兜底机制。能说清"静态优先、运行时兜底"的分工即抓住本题本质。

#
★★★

6. uni-app 的 Vue 技术栈统一与条件编译(#ifdef)机制

uni-app 如何实现 Vue 技术栈的多端统一?其条件编译(#ifdef)机制的原理和使用注意事项是什么?

  • uni-app 以 Vue 语法为统一 DSL,各端编译目标(小程序/H5/App)
  • 条件编译的预处理原理(注释中的 #ifdef/#ifndef/#endif 指令)
  • 条件编译的使用场景与注意事项

uni-app 以 Vue 单文件组件为统一开发语言,一套代码通过各端编译器分别产出小程序(微信/支付宝等)、H5 与 App(nvue/vue 双引擎)产物:编译时把 Vue 模板分别编译为目标端 DSL(小程序走模板转换、H5 走 web 组件、App 的 nvue 走原生渲染)。为了覆盖"同一套代码中的平台差异",uni-app 提供条件编译:以特定格式的注释(// #ifdef MP-WEIXIN ... // #endif 或 HTML 注释包裹)标记代码块,编译到指定平台时只保留对应块,其余平台编译时整块删除,本质是构建期的预处理指令,不是运行时的判断,因此未选中平台的代码不会进入产物。

使用场景:平台差异化 API 调用、不同端的页面配置、样式差异(如 #ifdef H5 中写 web 专属样式)、特定平台插件引用。注意事项:条件编译在 JS 中写在注释里,必须严格保持注释格式且不支持表达式(只能是平台标识宏);注释内代码仍会被编辑器的语法检查解析,需保证语法合法;过度使用会使代码碎片化、难以维护,应优先用 API 封装层收敛差异,条件编译只处理"无法封装"的结构级差异;各平台宏(MP-WEIXIN、H5、APP-PLUS 等)要区分大小写写准确。

核心是"构建期预处理":条件编译在编译时删代码,而非运行时判断,这是与 if(platform) 的本质区别。回答讲清指令语法、执行时机、适用场景与维护注意,即可覆盖考点。

#
★★

7. Skyline 渲染引擎与 WebView 渲染的差异与迁移成本

小程序 Skyline 渲染引擎与 WebView 渲染有何差异?迁移到 Skyline 需要评估哪些成本?

  • Skyline 自绘渲染 vs WebView 渲染的原理差异
  • 能力差异:组件、动画、滚动容器、同层渲染
  • 迁移成本:兼容性检查、组件替代、回归测试

传统小程序渲染层基于 WebView:WXML/WXSS 转成 HTML/CSS 渲染,受 WebView 能力限制(如复杂动画性能、滚动容器样式有限);Skyline 是微信自研的渲染引擎,不依赖 WebView,用自绘方式直接渲染小程序组件,提供更完整的能力:flex/grid 等布局更接近 web 标准、支持更丰富的样式与原生滚动容器(scroll-view 支持多种滚动模式)、动画性能更好(渲染层合成)、支持同层渲染与更细的渲染优化(如共享元素转场、自定义导航)。代价是 Skyline 与 WebView 渲染存在能力差异,部分特性(如某些 CSS 属性、web-view 相关能力、部分组件样式)不支持或不完全一致。

迁移成本评估:一是兼容性检查——用官方检查工具扫描项目对 Skyline 不支持特性的依赖(如某些选择器、组件属性、Canvas/WebView 使用);二是组件与样式适配——不支持的样式/组件需要替代实现或保持 WebView 页面;三是性能验证——迁移后需回归首屏、滚动、动画、内存等指标;四是双引擎并存——Skyline 支持页面级灰度开启(按页面配置),可在新页面试用 Skyline、存量页面保持 WebView,渐进迁移降低风险。收益明显(性能、动画、体验)但需要投入适配与回归,适合对滚动与动画体验要求高的核心页面先行。

答题框架:原理差异(自绘 vs WebView)→ 能力差异(布局/动画/滚动/同层渲染)→ 迁移成本(兼容扫描、样式替代、性能回归、灰度策略)。重点讲"不是免费升级,需按页面灰度适配"。

#
★★

8. Taro 跨端到 H5/RN 时的 API 抹平与差异处理策略

Taro 跨端输出 H5 与 React Native 时,如何抹平各端 API 差异?差异处理有哪些策略?

  • Taro 的 API 统一封装层(@tarojs/taro 平台实现分发)
  • H5/RN 端对原生 API 的降级与适配实现
  • 差异处理策略:条件编译、能力探测、插件扩展

Taro 通过统一的 API 命名空间(@tarojs/taro 的 Taro.request、Taro.getSystemInfo 等)封装平台差异:编译到不同端时,API 层按平台加载对应实现——小程序端直接映射小程序 API,H5 端用浏览器能力(fetch/axios、localStorage)实现,RN 端用 RN 原生模块(fetch、AsyncStorage)实现,做到"调用一致、实现各异"。无法直接对应的能力(如扫码、支付、蓝牙)在 H5/RN 端只能降级:抛异常、返回空数据或引导用 web 方案,Taro 通过 platformApi 机制允许开发者自定义各端实现。

差异处理策略分三层:一是"API 封装层抹平"——高频通用能力由框架统一适配;二是"条件编译兜底"——结构级或实现级差异用 TARO_ENV 条件编译分别写实现(如 H5 用 localStorage、小程序用 storage API 之外的差异化逻辑);三是"能力探测 + 降级"——运行期用 Taro.getEnv 或平台判断走不同分支,对不支持的能力提供降级体验。实践建议:把业务对端差异收敛在 service 层与自定义 hook 中,页面层尽量写纯业务代码;对低频差异 API 建立自己的包装模块,统一错误与降级行为。

主线是"框架封装 + 条件编译 + 能力探测"三层策略:统一 API 由框架分发实现,结构差异靠条件编译,运行时差异靠探测降级。能说出"调用一致、实现各异"的封装思想即可得分。

#
★★

9. uni-app x(uts 语言)的原生编译与性能优势

uni-app x 的 uts 语言是什么?它如何实现原生编译?相比传统 uni-app(js 运行时)有哪些性能优势与限制?

  • uts:TypeScript 风格、可编译到各端原生语言的中间语言
  • 编译路径:uts → kotlin/swift/原生渲染 的静态编译
  • 性能优势(无 JS 桥、原生渲染、AOT)与生态限制

uni-app x 是 uni-app 的下一代跨端方案,其核心是 uts 语言:一门语法近似 TypeScript 的语言,编译期被分别编译为各端原生代码——Android 端编译为 Kotlin、iOS 端编译为 Swift、Web 端编译为 JS,UI 采用各端原生渲染(Android 用 uni-app x 自研渲染引擎,不依赖 WebView),实现"一套代码、原生运行"。与 uni-app 传统方案的 JS 运行时 + 逻辑层/渲染层桥接不同,uts 代码直接成为原生代码,消除了跨语言桥接与序列化开销。

性能优势:启动更快(无 JS 引擎初始化与包解析)、渲染更快(原生组件直接渲染,无 WebView)、交互与动画更流畅、内存占用更可控、支持 AOT 编译与原生类型安全。限制:生态成熟度低于传统 uni-app(插件与三方库需要 uts/原生适配)、部分 JS 动态特性(动态类型、eval 等)不可用、语法需符合 uts 静态约束、对开发者有原生开发认知要求;平台能力差异仍需条件编译与原生插件补齐。适用场景:对启动与渲染性能要求高的 App 应用,团队可接受原生化约束。

抓住"uts 编译到原生"与"原生渲染"两个关键点讲性能优势(无桥接、无 WebView),再客观讲限制(生态与动态特性),即完整。对比传统 uni-app 的"JS 运行时 + 桥接"是答题骨架。

#
★★

10. 小程序首屏渲染优化,骨架屏、分包预下载、数据预拉取

小程序首屏渲染优化有哪些手段?骨架屏、分包预下载、数据预拉取分别解决什么问题、如何实施?

  • 首屏渲染链路的瓶颈(包下载、JS 执行、数据请求、渲染)
  • 骨架屏减少白屏感、分包预下载减少加载等待
  • 数据预拉取(预请求/提前缓存)减少首屏数据等待

小程序首屏时间由"包下载 → 逻辑层 JS 执行与初始化 → 数据请求 → 渲染层首帧"组成,优化手段对应各环节。骨架屏:在真实内容未就绪时渲染与最终布局一致的占位结构,减少白屏带来的焦虑感与布局跳动(用 CSS 渐变/背景动画实现,或用工具从页面生成骨架代码),本质是"感知优化",不减少真实耗时,但显著提升体验指标(LCP 感知)。分包预下载:把非首屏模块放入分包,配置分包预下载规则(preloadRule),在进入主包页面时后台静默下载常用分包,既保证主包首屏小、又降低分包页跳转等待。

数据预拉取:利用小程序的数据预拉取能力(如微信的"数据预拉取"配置或业务侧在 app 启动时提前发起请求并缓存),在页面 onLoad 前把首屏数据拿到并缓存,页面渲染时直接用缓存,减少"渲染等待网络"的时间;配合请求缓存与本地持久化(storage),实现"秒开"的弱网体验。三者组合:分包/预下载解决"包"、预拉取解决"数据"、骨架屏兜底"感知",再叠加精简首屏 JS 执行(按需注入)与 setData 优化,形成完整优化链路。

按"包-数据-感知"三层讲三类手段的定位:分包预下载治包体、数据预拉取治数据等待、骨架屏治白屏感知,最后说明三者互补组合,即可覆盖。能对应到首屏时间分解(下载/执行/请求/渲染)是加分项。

#
★★

11. 长列表虚拟化(recycle-view)的实现与回收机制

小程序长列表虚拟化(如 recycle-view)的实现原理是什么?它的节点回收与复用机制如何工作?

  • 虚拟列表的核心:只渲染可视区节点 + 占位撑高
  • recycle-view 的节点回收(回收到池)与复用机制
  • 滚动位置保持与数据更新的处理

长列表不能一次性渲染全部节点(节点数与渲染耗时、内存线性增长),虚拟化思路是"只渲染可视区域附近的节点,用占位元素撑起总高度":外层容器高度固定,内部根据滚动偏移计算当前应渲染的节点区间,未渲染的区域用空白占位(或按行高精确占位),滚动时动态替换渲染内容,从而把 DOM 节点数控制在可视区数量级。小程序中 recycle-view 由框架(如 recycle-view 插件、Skyline 的 scroll-view 虚拟化)实现,原理与 Web 虚拟列表一致,但需适配小程序 setData 与渲染层机制。

回收与复用机制:框架维护"渲染池"(已创建但滚出屏幕的节点实例),滚动时滚出的节点不是销毁而是回收入池,滚入视口的行优先从池中复用旧实例、仅更新其数据(setData 更新行内字段),避免重复创建组件带来的初始化开销,这就是"节点回收复用";占位符负责维持滚动高度,因此数据长度(预估高度)要准确,否则滚动条跳动。注意点:虚拟列表要求每行高度稳定或提供高度缓存,行内若含图片等异步内容需处理高度变化;配合 setData 只更新可视行数据,避免全量提交;滚动定位(如回到顶部、跳转到某条)需基于总高度换算实现。

核心讲清"可视区渲染 + 占位撑高 + 节点池复用"三件事:占位解决高度、池解决创建开销、数据更新解决内容正确。再补高度稳定要求与滚动定位换算,即完整覆盖实现与机制。

#
★★

12. 小程序内存占用监控与页面栈深度(10 层)限制应对

小程序内存占用如何监控与治理?页面栈深度上限(约 10 层)的限制如何应对?

  • 内存监控手段(性能面板、API、崩溃率观察)与内存泄漏治理
  • 页面栈 10 层限制的语义与触发条件
  • 应对:跳转方式选择(navigateTo/redirectTo/reLaunch)、栈管理

小程序内存占用主要来自页面实例、逻辑层数据、图片与渲染层节点。监控手段:开发者工具的性能面板与真机调试查看内存曲线,线上通过 wx.getPerformance 等能力采样、配合崩溃率与 OOM 报告观察内存异常,重点排查"数据只增不减"(页面销毁后定时器未清理、全局变量持有页面引用、大图片未释放、storage 无限膨胀)。治理:及时清理监听与定时器、控制 setData 数据量与频率、图片按尺寸压缩与懒加载、限制全局数据、大列表用虚拟化。

页面栈深度限制:微信小程序页面栈上限 10 层,超出时 navigateTo 失败并提示。应对策略:一是按业务选择跳转方式——普通进栈用 navigateTo(可返回),栈深容易增长的业务链改用 redirectTo(关闭当前页再进栈,不增加深度)或 reLaunch(清空栈重建);二是"平级页尽量不进栈",如从列表进入详情后,详情页内的再次跳转评估是否用 redirectTo;三是复杂流程(如多步表单)用单页面内部状态切换代替多级页面;四是 tab 切换用 switchTab 不占栈。设计时还要考虑返回逻辑,redirectTo 后返回会直接跳过被替换页面,需按用户预期设计。

分两个问题回答:内存监控与治理(数据/图片/清理)、页面栈 10 层应对(跳转方式选择与栈设计)。关键是把 navigateTo/redirectTo/reLaunch 的栈语义讲清楚,这是应对深度限制的根本手段。

#
★★

13. 小程序与 WebView H5 混合开发的通信(JSSDK/postMessage)

小程序内嵌 WebView(web-view)加载 H5 时,小程序与 H5 之间如何通信?postMessage 与 JSSDK 的机制与限制是什么?

  • web-view 组件加载 H5 与消息方向(小程序→H5、H5→小程序)
  • postMessage 与 bindmessage 事件、JSSDK wx.miniProgram 接口
  • 通信限制(时机、域名校验、数据序列化)

小程序用 web-view 组件内嵌 H5 页面,通信分两个方向:H5→小程序,通过微信 JSSDK(在 H5 中引入 jweixin 脚本)调用 wx.miniProgram.postMessage({data}) 发送消息,小程序侧在 web-view 的 bindmessage 事件中接收(注意:postMessage 的消息不会立即触发,只有在小程序后退、分享、组件销毁时才触发一次 bindmessage,需要"即时通信"场景要换用 URL 参数或 navigateTo 传递);小程序→H5,通过 web-view 的 src 动态拼接 URL 参数,H5 在 load 时解析,或通过 wx.miniProgram.navigateBack/navigateTo 控制跳转实现间接传参。

限制与注意:一是 web-view 加载的域名必须在业务域名白名单中配置且校验 HTTPS;二是 H5 中调用小程序能力受 JSSDK 权限与 scope 限制,不是所有 JSSDK 接口都能在小程序 web-view 中调用;三是消息传递有延迟语义(bindmessage 触发时机),高频数据不适合走该通道;四是数据需序列化为可 JSON 化的格式,大对象与二进制要单独处理。实践上:低频命令用 postMessage,参数同步用 URL,实时数据考虑用服务端中转或小程序侧原生请求后传参,形成分层通信方案。

先分方向讲机制(JSSDK postMessage → bindmessage;URL 参数 → H5 解析),再重点讲 bindmessage 的延迟触发时机这个"坑",最后给分层通信建议,即可覆盖。

#
★★

14. 微信开放数据域(排行榜)的隔离机制与主域通信(sharedCanvas)

微信小游戏/小程序的开放数据域(如排行榜)为何要隔离?它与主域如何通过 sharedCanvas 通信?

  • 开放数据域的安全隔离动机(数据防篡改)
  • sharedCanvas 共享画布机制与数据渲染流程
  • 通信限制与设计约束(消息、数据量、绘制)

开放数据域(Open Data Context)用于展示好友关系链数据(排行榜、好友互动),为防开发者篡改关系链数据,微信把关系链数据隔离在独立 JS 上下文(开放数据域)中运行:该域只能通过 wx.getFriendCloudStorage 等受限 API 获取关系链数据,代码受约束(不能直接操作主域对象、不能访问部分全局能力),主域也无法直接拿到原始关系链数据,只能拿到"绘制结果",从而保证数据可信。

通信机制是 sharedCanvas(共享画布):开放数据域把排行榜等绘制到 sharedCanvas 上,主域获取同一 sharedCanvas 再将其绘制到自己的 Canvas(或通过纹理/截图展示),即"开放域画→主域展示"。操作流程:主域创建 Canvas 并获取 sharedCanvas 上下文,开放数据域同样拿到该共享画布上下文绘制内容,主域渲染时把 sharedCanvas 作为图像源绘制到可见画布(或转成图片后用于纹理),实现跨上下文的内容共享。设计约束:共享画布尺寸需两端一致、开放域绘制性能有限(避免高频重绘)、通信只能通过画布内容或受限消息机制(小游戏有开放数据域消息通道),不适合传输大结构化数据;主域对关系链数据的使用必须通过"绘制"这一间接途径。

答题主线:隔离动机(防篡改、数据可信)→ sharedCanvas 机制(开放域绘制、主域展示)→ 约束(尺寸一致、性能、通信受限)。讲清"数据不能出域、内容通过画布出域"即抓住本质。

#
★★

15. 小程序监控埋点与异常上报体系设计

如何为小程序设计一套监控埋点与异常上报体系?需要覆盖哪些维度、注意哪些限制?

  • 监控维度:页面性能、JS 异常、接口请求、业务埋点
  • 小程序限制下的采集方式(生命周期、网络、后台运行)
  • 上报通道、采样与去重、以及隐私合规

监控体系分四个维度:一是页面性能——onLoad/onShow 时间、首屏渲染耗时、setData 耗时、内存曲线,用生命周期与 performance API 采集;二是 JS 异常——通过 wx.onError / App.onError 与页面 onError 捕获运行时错误、Promise 未捕获与组件错误,携带页面栈与调用栈上报;三是接口监控——统一封装 request 层记录成功/失败、耗时、状态码、错误信息,区分网络异常与业务错误;四是业务埋点——行为事件(点击、曝光、转化)按统一事件模型(事件名+属性+公共参数)采集。上报需批量合并(定时批量或队列)、失败重试(本地缓存待网络恢复补报)与采样(高频事件按比例采样)。

小程序环境限制:无后台长跑能力(切后台后定时器停止),上报队列要在 onHide/onUnload 时 flush;网络请求需配置合法域名;隐私合规上,埋点涉及用户信息(openid、行为轨迹)需遵循隐私协议声明与授权,数据脱敏、最小化采集;避免埋点影响性能——上报逻辑异步化、不阻塞主流程、控制单条数据大小。体系设计上还要有上报 SDK 的版本管理、数据校验与线上可观测(通过监控大盘查看崩溃率、页面耗时、接口错误率),形成"采集-上报-分析-告警"闭环。

按"性能/异常/接口/业务"四维采集 + "批量、重试、采样"上报策略 + "后台限制、域名、合规"约束三层组织答案,再补闭环建设即可。突出小程序特性约束(后台停定时器、域名白名单)是加分点。

#
★★

16. 小程序支付与虚拟支付的合规限制(iOS 虚拟商品)与 IAP 替代方案

小程序内支付在 iOS 上对虚拟商品有什么合规限制?如何设计替代方案?

  • iOS 虚拟商品支付限制(虚拟支付/虚拟服务不得用非 IAP 通道)
  • 微信小程序 iOS 端虚拟支付(wx.requestVirtualPayment)的合规要求
  • 替代方案:引导 H5/公众号/App 支付、积分/代币体系、客服代充

苹果审核条款规定:数字内容与服务(虚拟商品,如会员、金币、电子书、直播打赏等)必须使用 Apple 的 IAP(应用内购买)通道并抽成,绕过 IAP 的支付通道在 iOS 端不被允许。微信小程序在 iOS 端对虚拟支付有专门限制:早期直接不支持 iOS 端虚拟支付,后续开放 wx.requestVirtualPayment(微信虚拟支付,仅对部分类目/资质开放)作为合规通道,且要求虚拟商品按 IAP 规则接入;实体商品、线下服务等非虚拟支付不受此限。因此在 iOS 端做虚拟商品支付必须走合规通道,否则面临封禁与审核风险。

替代与变通方案:一是接入微信官方虚拟支付能力(wx.requestVirtualPayment),按开放类目与资质申请,走苹果 IAP 合规链路;二是引导用户到非 iOS 受限通道完成购买——如分享卡片/短信引导到 App、公众号 H5 或小程序码打开"非 iOS 环境"购买(注意苹果对"引导绕过 IAP"的判定,需谨慎合规);三是设计积分/代币/免费解锁体系规避直接虚拟支付,或在 iOS 端隐藏虚拟商品入口、仅展示实体与线下服务;四是客服手动代充(人工处理,规模化受限)。设计时需区分商品类型(虚拟 vs 实物)、按平台差异做开关与提示,并与法务确认合规边界。

先讲清 iOS 虚拟商品必须走 IAP 的规则与微信虚拟支付通道,再列替代方案并强调合规红线。能区分"虚拟商品 vs 实体商品"与"合规通道 vs 变通方案"即体现对支付合规的理解。

#
★★

17. 小程序隐私授权演进,getUserProfile 废弃后的头像昵称填写能力、隐私协议弹窗与用户信息授权新规的合规适配?

微信小程序 getUserProfile 废弃后,头像昵称如何获取?隐私协议弹窗与用户信息授权新规对开发有什么影响,如何合规适配?

  • getUserProfile 废弃后的头像昵称填写能力(button open-type 与 input 组件)
  • 隐私协议弹窗(隐私保护指引)的接入时机与必要性
  • 授权新规(按需授权、最小化收集)的合规适配

自基础库调整后,getUserProfile 已不能返回真实头像昵称(返回灰色头像与"微信用户"),官方改为"头像昵称填写能力":头像通过 button 的 open-type="chooseAvatar" 触发头像选择(返回临时头像路径,上传后使用),昵称通过 input 组件设置 type="nickname" 由用户自主填写(键盘自动带上昵称候选),两者均由用户主动触发、自主填写,满足最小化收集原则。同时仍可用 wx.getUserInfo 的受限版本获取基础信息,但需用户主动授权。

隐私合规新规:小程序需在 app.json 声明 privacy 相关配置并在管理后台配置《隐私保护指引》,首次涉及用户信息(如获取头像、位置、手机号等)时需主动弹出隐私协议弹窗(wx.requirePrivacyAuthorize 或系统自动弹窗),用户同意后才能调用对应 API,否则 API 失败(fail 中带隐私未授权错误码);开发者需按弹窗结果控制功能入口,提供"去设置"引导。适配要点:梳理全部个人信息相关 API 清单、在授权前不调用敏感接口、设计拒绝授权的降级体验(如未授权时显示占位头像与默认昵称)、对用户拒绝后再次触发的引导流程,并配合《小程序用户隐私保护指引》的类目声明做到最小化、明确目的采集。

分两段答:头像昵称的新获取方式(chooseAvatar + nickname input,强调用户主动),隐私协议弹窗与按需授权适配(声明、弹窗时机、降级体验)。抓住"用户自主填写、最小化收集、拒绝降级"三条主线即合规完整。

#
★★

18. 原生组件与同层渲染,原生组件层级高于 web-view 的历史问题、cover-view/cover-image 的适配与同层渲染后的变化?

小程序原生组件(如 video、map)曾经存在层级高于 web-view 的问题,cover-view/cover-image 是做什么的?同层渲染出现后这些问题如何变化?

  • 原生组件脱离 WebView 渲染导致的层级覆盖问题
  • cover-view/cover-image 的覆盖层适配方案及其局限
  • 同层渲染(原生组件嵌入 WebView 渲染层)后的变化与残余边界

早期小程序中 video、map、canvas、textarea 等原生组件由原生层渲染,不在 WebView 的文档流内,导致两个问题:一是原生组件层级始终高于普通页面元素(如弹窗、悬浮按钮无法覆盖在 video 之上);二是原生组件内部无法嵌套普通元素(video 上放自定义文字/按钮不可行)。为此平台提供 cover-view/cover-image:专用于覆盖在原生组件之上的"覆盖层"组件,可把文字、按钮等画在原生组件上层,实现如播放器控制条、地图标记等交互,但能力受限(样式支持有限、不能覆盖任意位置、性能开销)。

同层渲染出现后:平台把原生组件"嵌入"WebView 渲染层,成为普通节点参与层级与排版,video/map 等可被普通元素覆盖、内部可嵌套普通 view,开发体验与 Web 一致,cover-view 的很多场景被原生组件同层渲染取代。但仍有边界:部分组件(如 camera 摄像头、部分场景的 map)仍是原生层级、同层能力随基础库与机型有差异,历史兼容仍需 cover-view 兜底;同层渲染也引入性能与兼容成本(如 video 同层后的滚动、嵌套布局需要适配)。适配原则:新页面优先用同层能力,老页面与特殊组件保留 cover-view 兜底,并做真机分级验证。

按"问题(层级高于 web-view)→ 过渡方案(cover-view 覆盖层)→ 演进(同层渲染)→ 残余边界"的时间线回答,能体现对平台渲染机制演进的理解。重点讲清同层渲染解决的是"层级与嵌套"两个问题。

#
★★

19. 小程序启动流程(冷启动/热启动/按需注入/用时预拉取)

小程序冷启动与热启动的流程有何不同?按需注入与用时预拉取(preloadRule)如何优化启动性能?

  • 冷启动(重新下载/初始化)与热启动(从后台恢复)的流程差异
  • 按需注入(lazyCodeLoading)减少首包 JS 执行
  • 用时预拉取(preloadRule)提前加载分包资源

冷启动指小程序首次被打开或进程被杀死后重新启动,流程为:下载代码包(首次或更新)→ 创建逻辑层与渲染层 → 注入并执行 JS(App/Page 注册)→ 渲染首屏;热启动指小程序从后台回到前台(进程存活),直接复用已有逻辑层实例触发 onShow,无需重新下载与初始化,启动明显更快。工程上应尽量把用户带回的路径走热启动(保持后台存活、避免进程被杀),同时优化冷启动的每一步耗时。

优化手段:一是按需注入(lazyCodeLoading: "requiredComponents"):默认小程序首次启动会把所有页面/组件代码注入执行,开启按需注入后仅注入首屏页面与组件代码,其他页面用时再注入,显著减少启动 JS 执行时间(包体越大收益越大),但需注意页面首次跳转的注入延迟;二是用时预拉取(preloadRule):在 app.json 声明"进入某页面时预下载哪些分包",让用户在即将访问分包页前后台完成分包下载,避免分包页跳转时的下载等待;三是配合首屏数据预拉取(启动时预请求)、压缩包体(分包、图片压缩)与控制同步初始化逻辑,共同缩短"下载-注入-渲染"链路。还需注意:按需注入下 App.onLaunch 里引用的非首屏模块要避免误删依赖。

先对比冷热启动流程差异(下载/初始化 vs 复用实例),再分别讲按需注入(减首屏 JS 执行)与用时预拉取(预下载分包)两个官方能力的作用与注意点,即可覆盖启动优化考点。

#
★★

20. 分包加载、独立分包与分包预下载的设计与限制

小程序分包加载、独立分包与分包预下载分别是什么?在设计与使用上各有哪些限制?

  • 主包+分包的结构与主包体积限制
  • 独立分包(不依赖主包即可运行)的语义与限制
  • 分包预下载(preloadRule)的配置与约束

分包加载:小程序有主包/分包体积限制(如微信主包不超过 2M、总包不超过 20M,可配置扩展),为控制启动下载量,把非首屏页面按业务拆入分包,每个分包独立打包,首次启动只下载主包+必要资源,进入分包页时才下载对应分包,从而减少首屏加载。设计要点:tabBar 页面、核心流程页面放主包;按业务/按功能切分分包,控制分包间依赖(分包可引用主包资源,主包不能引用分包代码,分包间默认不能互相引用,需用分包异步化)。

独立分包:可不依赖主包独立运行的分包,进入独立分包时若主包未下载,只下载该独立分包即可启动(主包可在后台下载),适合分享落地页、活动页等"不想被主包拖累"的场景;限制:独立分包不能引用主包资源/自定义组件,独立分包内不能跳转普通分包页(只能跳独立分包与主包 tab 页)。分包预下载:通过 preloadRule 配置"进入某页面时预下载指定分包",在用户闲时/跳转前提前下载分包,减少分包页首跳等待;限制:预下载配置针对分包级、触发时机是进入配置的页面时,需权衡预下载流量与收益,且预下载的分包也要在下载成功后走正常分包缓存。三者组合:主包做小、分包按需、独立分包解耦分享场景、预下载抹平跳转等待。

逐一讲清"分包(体积与按需下载)、独立分包(不依赖主包独立启动)、预下载(提前拉取)"的语义、适用场景与限制(依赖方向、体积、跳转约束),即可覆盖。指出"主包不能引用分包、独立分包不能引主包资源"的依赖规则是得分点。

#
★★

21. 小程序登录态(code 换 session_key/openid)与 UnionID 体系

小程序登录态是如何建立的?code 换 session_key 的流程与安全注意事项是什么?UnionID 体系解决什么问题?

  • wx.login 获取 code → 后端 code2session 换 openid/session_key
  • session_key 的用途(解密手机号、用户数据)与安全存储
  • UnionID 打通同一用户多平台/多应用身份

小程序登录标准流程:前端 wx.login 获取临时凭证 code(有效期短、一次有效),把 code 传给自有后端,后端调用微信接口 code2session,用 code + appid + appsecret 换取 openid(用户在小程序内的唯一标识)与 session_key(会话密钥),后端建立自己的登录态(如签发 token/设置 session),返回给前端,此后业务请求携带自建 token 由后端校验,实现"登录态自建、openid 服务端持有"。

安全要点:code 必须由后端使用并一次性消费,前端不能接触 session_key 与 appsecret;session_key 用于解密敏感数据(如 getPhoneNumber 手机号、加密数据解密),应只保存在服务端且随 code 失效而失效;登录态 token 要设置过期与刷新机制、绑定设备与用户,防止重放;敏感操作需二次校验。UnionID:同一微信用户在"同一开放平台账号下的多个小程序/公众号/App"中拥有相同 UnionID,用于打通多应用身份(如小程序与 App 共享用户体系),获取方式:绑定开放平台账号后,code2session 返回或用户信息中携带 unionid;UnionID 是"跨应用识别同一用户"的键,openid 是"单应用内唯一"的键,两者配合可实现统一账号体系与数据打通。

按"code→openid/session_key→自建登录态"链路讲流程,再强调安全边界(code 一次有效、session_key 与 appsecret 不落前端),最后讲 UnionID 的跨应用打通价值与 openid/unionid 的区别,即可完整覆盖。

#

22. 不同小程序平台(微信/支付宝/抖音)API 差异的适配层设计

微信、支付宝、抖音等小程序平台的 API 存在哪些差异?如何设计一套跨平台适配层?

  • 各平台 API 命名、参数、能力与限制的差异
  • 适配层设计:统一接口、平台分发、差异清单
  • 能力探测、降级与条件编译的配合

多平台小程序差异体现在:一是 API 命名与参数不同(如登录 wx.login vs my.getAuthCode vs tt.login,存储 wx.setStorageSync vs my.setStorageSync);二是能力差异(支付、分享、开放数据能力各平台独立且需各自申请);三是生命周期与组件差异(onShow 参数、导航栏配置、部分组件属性不一致);四是审核与规范差异(隐私、类目)。若直接按平台写业务代码,维护成本随平台数线性爆炸。

适配层设计思路:定义统一 API 契约(如 login、getUserInfo、request、storage、pay 的统一签名与返回结构),用"平台分发器"实现——运行时根据 getSystemInfo/getEnv 判断平台(或编译期宏),把统一调用映射到各平台的实现(如 adapterFactory 注册各平台实现,公共层只依赖统一契约);差异用三层处理:第一层统一封装(请求、存储、登录等高频 API 完全抹平),第二层能力探测(getSystemInfoSync 探测能力与版本,不支持则降级或隐藏入口),第三层条件编译兜底(结构级差异用 #ifdef 分别写)。配套维护"平台差异清单"文档,收敛每个平台的特有 API 调用到独立模块,业务层禁止直接调平台 API,保证新增平台时只改适配层。风险控制:各平台真机联调、能力灰度验证,适配层自身要有完整测试用例。

答题主线是"统一契约 + 平台分发 + 能力探测 + 条件编译"四层适配,强调"业务层不感知平台、差异收敛在适配层"。能举例说明具体差异(wx.login vs my.getAuthCode)并给出分层方案即完整。