小程序面经
核心考点
1. 双线程架构
- 渲染层(WebView):负责 WXML/WXSS 渲染,每个页面一个 WebView
- 逻辑层(JS Engine / V8):执行业务逻辑,独立线程,无法直接操作 DOM
- 通信:通过 JSBridge 与 Native 通信,数据经序列化传递
- 优点:安全(逻辑层无法直接操作 UI);缺点:通信有延迟,setData 数据量大性能差
2. 生命周期
App: onLaunch → onShow → onHide → onError
Page: onLoad → onShow → onReady → onHide → onUnload
- onLoad:只执行一次,接收页面参数
- onReady:页面初次渲染完成
- onShow / onHide:前后台切换时触发
3. 性能优化
- setData 优化:只传变化的数据,避免大对象;使用路径更新
'list[0].name' - 列表渲染:
wx:key必须设置,避免重复渲染 - 图片懒加载:
lazy-load属性 - 分包加载:主包 < 2MB,总包 < 20MB,按需加载分包
- 骨架屏:提升首屏体验
4. 与 H5 / App 的区别
| 对比 | 小程序 | H5 | App |
|---|---|---|---|
| 运行环境 | 微信/支付宝等宿主 | 浏览器 | 原生系统 |
| 发布 | 需审核 | 随时 | 需审核 |
| 能力 | 受宿主限制 | 受浏览器限制 | 几乎无限制 |
| 体验 | 接近原生 | 较差 | 最好 |
5. 跨端框架对比
| 框架 | 原理 | 优缺点 |
|---|---|---|
| Taro | 编译时转换 | React 语法,生态好,编译有差异 |
| uni-app | 运行时 + 编译 | Vue 语法,覆盖平台多 |
| mpx | 增强编译 | 贴近原生写法,性能好 |
6. 高频面试题
- 小程序登录流程:wx.login 获取 code → 后端换取 openid + session_key → 返回自定义 token
- WXS 是什么:在渲染层运行的脚本,可减少逻辑层通信,提升性能
- 自定义组件通信:父→子 properties;子→父 triggerEvent;兄弟通过父或 EventBus
- 为什么不能用 window/document:逻辑层没有 DOM/BOM 环境
- 微信支付流程:后端下单 → 返回支付参数 → 前端 wx.requestPayment → 回调处理
微信小程序实战面经
实战高频考点
1. 登录与鉴权完整流程
前端 wx.login() → 获取 code
↓
发送 code 到自己的后端
↓
后端调用微信接口:code2Session
→ 返回 openid + session_key + unionid
↓
后端生成自定义 token,存 Redis
↓
前端存 token 到 storage,后续请求携带
常见问题
session_key有有效期,需定期调用checkSession验证wx.getUserInfo已废弃,用wx.getUserProfile(需用户触发)unionid需满足:已关联公众号/小程序,或绑定开放平台
2. 支付流程
1. 前端发起支付请求到自己后端
2. 后端调用微信统一下单接口 → 返回 prepay_id
3. 后端用 prepay_id 签名生成支付参数
4. 前端调用 wx.requestPayment({ timeStamp, nonceStr, package, signType, paySign })
5. 用户支付完成 → 微信回调后端 → 后端更新订单状态
6. 前端 success 回调只做提示,不以此为支付成功依据(以后端通知为准)
3. 自定义组件高级用法
behaviors(类似 mixins)
const loginBehavior = Behavior({
data: { isLogin: false },
methods: {
checkLogin() { /* ... */ }
},
lifetimes: { attached() { this.checkLogin() } }
})
Component({
behaviors: [loginBehavior],
// 自动拥有 loginBehavior 的数据和方法
})
抽象节点(类似 slot 的高阶用法)
{ "componentGenerics": { "selectable": { "default": "./default-item" } } }
4. 小程序云开发
- 云函数:Node.js 环境,免鉴权调用微信 API,自动获取 openid
- 云数据库:文档型数据库,支持实时监听
- 云存储:文件上传下载,
wx.cloud.uploadFile - 云调用:云函数中调用微信开放能力(发送订阅消息等)
// 云函数端
exports.main = async (event, context) => {
const { openid } = context.OPENID // 自动获取,无需手动传
return db.collection('users').where({ _openid: openid }).get()
}
5. 分包策略详解
{
"subpackages": [
{ "root": "packageA", "pages": ["pages/cat", "pages/dog"] },
{ "root": "packageB", "pages": ["pages/apple"] }
],
"preloadRule": {
"pages/index": { "network": "all", "packages": ["packageA"] }
}
}
- 独立分包:可不依赖主包运行(分享页、广告页)
- 分包预加载:用户在某页面时提前加载指定分包
6. 调试与发布
- vconsole:小程序内置调试面板,
wx.setEnableDebug({ enableDebug: true }) - 抓包:Charles/Whistle 配合代理,需在微信开发者工具开启
- 体验版/预览版:真机测试不经过审核
- 灰度发布:后台配置按比例放量
7. 高频面试题
- 小程序如何做埋点? 封装统一 Page/Component,重写 onShow/tap 等生命周期
- 如何实现全局错误监控?
App.onError+wx.onError捕获未处理异常 - 小程序能访问 cookie 吗? 不能,用
wx.setStorageSync替代 - 如何做长连接?
wx.connectSocketWebSocket API - 页面间传参的方式? URL 参数、globalData、EventBus、本地存储
跨端框架深度对比(Taro / uni-app / Flutter)
跨端技术选型
1. Taro 深入
技术架构
开发语言:React / Vue
|
Taro 编译器
/ | \
微信 支付宝 H5 RN
小程序 小程序 Web 原生应用
Taro 3.x 运行时架构(重要变化)
- 老版本:编译时转换,将 JSX 转成小程序模板语言
- Taro 3.x:运行时适配,小程序运行 React/Vue 全量,通过运行时模拟 DOM API
- 优点:支持 React 全部特性;缺点:包体积大、性能差一些
Taro 特性
// 内置组件:跨端封装
const App = () => (
<View className="container"> {/* 转为 div / wx:view */}
<Text>Hello</Text> {/* 转为 span / wx:text */}
<Image src={img} /> {/* 转为 img / wx:image */}
<ScrollView scrollY> {/* 转为 各平台滚动组件 */}
{list.map(item => (
<View key={item.id}>{item.name}</View>
))}
</ScrollView>
</View>
)
// 平台判断
import Taro from '@tarojs/taro'
if (Taro.getEnv() === Taro.ENV_TYPE.WEAPP) {
// 微信小程序独有逻辑
}
性能优化
- 采用
@tarojs/plugin-html可直接使用 HTML 标签 - 长列表用
VirtualList组件 - 采用分包加载减小主包体积
2. uni-app 深入
技术架构
开发语言:Vue 3
|
uni-app 编译器
/ | | \
微信 支付宝 H5 App
| |
各家小程序 iOS/Android
<!-- 页面组件 -->
<template>
<view class="container">
<!-- 条件编译:不同平台显示不同内容 -->
<!-- #ifdef MP-WEIXIN -->
<text>微信小程序独有内容</text>
<!-- #endif -->
<!-- #ifdef H5 -->
<text>H5独有内容</text>
<!-- #endif -->
<text>所有平台共有</text>
</view>
</template>
uni-app 的 App 端
- H5+(HTML5 Plus)运行时:WebView 内嵌小程序逻辑 + 原生渲染
- nvue:基于 Weex 的原生渲染,性能较好
- uts(TypeScript 超集):编写原生插件,能调用原生 API
3. Flutter
核心架构
Dart 代码
|
Flutter 引擎
/ \
IOS Android
原生 原生
实现 实现
Flutter vs 小程序跨端
- Flutter 不依赖小程序容器,更适合面向 App 开发
- 小程序跨端(Taro/uni-app)主要面向微信、支付宝等平台
- Flutter 性能更接近原生,小程序跨端更快上手
4. 三种方案综合对比
| 对比项 | Taro 3.x | uni-app | Flutter |
|---|---|---|---|
| 开发语言 | React/Vue | Vue | Dart |
| 跨小程序 | 5家+ | 10家+ | 不支持 |
| 跨原生 App | RN | Weex | 完整支持 |
| 性能 | 中 | 中 | 高 |
| 包体积 | 较大 | 中 | 大 |
| 社区 | 活跃 | 活跃 | 活跃 |
| 适用场景 | 前端团队 | 小程序为主 | App 为主 |
5. 常见坏呓与解决
Taro 常见问题
1. 样式差异:小程序不支持部分 CSS 选择器,用 Taro 内置样式集或注意兼容性
2. 事件处理差异:小程序事件对象属性与 H5 不同,用内置组件
3. 包体积过大:按需加载、分包加载
4. Redux/Zustand 在微信小程序端需配置特殊适配
uni-app 常见问题
1. 条件编译容易遗忘:/* #ifdef MP-WEIXIN */ 由编译器处理
2. nvue 与 Vue 的样式差异: nvue 采用 Flex 布局,不支持所有 CSS
3. App 端原生能力需要通过 uts 或和 Native 插件实现
6. 资深面试题
- Taro 3.x 的运行时适配分导是什么思路?
- 小程序没有真实 DOM,运行时实现了一套虚拟 DOM API
- React/Vue 操作虚拟 DOM, Taro 将虚拟 DOM 映射为 setData 调用
- 比编译时转换灵活,但性能有损耗
- 如何选择跨端框架?
- 已有 React 团队且面向小程序 -> Taro
- 已有 Vue 团队且小程序为主 -> uni-app
- 面向 iOS/Android App,小程序不是主要 -> Flutter
- 需要高性能原生 App -> 纯原生开发
- 跨端框架如何处理平台差异?
- 条件编译(Taro: process.env.TARO_ENV,uni-app: #ifdef)
- 抓象平台能力到中间层,应用层不写平台相关代码
小程序性能深度优化实战
小程序性能优化深度
1. setData 性能优化
为什么 setData 影响性能?
- 逃逸层线程不能直接操作 DOM
- setData 数据经过序列化 -> Native 通信 -> 渲染层更新
- 数据量大、调用频繁就会成为瓶颈
优化实践
// 1. 只传必要的字段
const { list, count } = this.data
// bad
this.setData({ list: newList }) // 传辘1000条数据
// good
this.setData({ 'list[5].status': 'done' }) // 只传变化的字段
// 2. 减少 setData 次数(批量合并)
// bad
this.setData({ count: 1 })
this.setData({ loading: false })
// good
this.setData({ count: 1, loading: false })
// 3. 防抖高频调用(scroll 事件)
let timer
onScroll(e) {
clearTimeout(timer)
timer = setTimeout(() => {
this.setData({ scrollTop: e.detail.scrollTop })
}, 100)
}
// 4. 分屏渲染(分批设置大列表)
async function renderBatch(list) {
const BATCH = 20
for (let i = 0; i < list.length; i += BATCH) {
this.setData({ [`list`]: list.slice(0, i + BATCH) })
await new Promise(r => setTimeout(r, 16)) // 一帧的时间
}
}
2. 页面渲染优化
骨架屏
<!-- 骨架屏:首屏删叨剩留作平衡 -->
<view wx:if="{{loaded}}">
<real-content />
</view>
<view wx:else class="skeleton">
<view class="skeleton-avatar" />
<view class="skeleton-line" />
<view class="skeleton-line short" />
</view>
图片懒加载
<!-- 小程序内置支持 -->
<image lazy-load="{{true}}" src="{{item.url}}" />
<!-- 街道模式:Intersection Observer -->
// IntersectionObserver 实现懒加载
Page({
onReady() {
const observer = this.createIntersectionObserver()
observer.relativeToViewport({ bottom: 100 })
observer.observe('.load-more', (res) => {
if (res.intersectionRatio > 0) {
this.loadMore()
}
})
}
})
3. 分包策略深度
主包优化策略
{
"pages": [
"pages/index/index",
"pages/home/home"
],
"subpackages": [
{
"root": "packageUser",
"name": "user",
"pages": ["pages/profile", "pages/settings"]
},
{
"root": "packageOrder",
"name": "order",
"pages": ["pages/list", "pages/detail"]
}
],
"preloadRule": {
"pages/index/index": {
"network": "wifi",
"packages": ["user"]
}
}
}
分包最佳实践
- 主包只放起始页和公用组件、工具库
- 按功能模块划分分包
preloadRule配置分包预加载- 独立分包用于分享页、广告部置
4. 内存与渲染性能
内存优化
// 1. 图片内存管理——避免重复创建
const imageCache = new Map()
function getImage(url) {
if (!imageCache.has(url)) {
imageCache.set(url, wx.createImage ? wx.createImage() : null)
}
return imageCache.get(url)
}
// 2. 组件销毁时清理引用
Component({
lifetimes: {
detached() {
this._timer && clearInterval(this._timer)
this._observer && this._observer.disconnect()
}
}
})
// 3. 大数据分页加载,不要一次性读取全量
Page({
data: { list: [], page: 1, hasMore: true },
async loadMore() {
if (!this.data.hasMore) return
const res = await api.getList({ page: this.data.page })
this.setData({
list: [...this.data.list, ...res.data],
page: this.data.page + 1,
hasMore: res.hasMore
})
}
})
5. 小程序性能分析工具
1. 微信开发者工具 Audits 面板
- 体验评分:启动性能、页面渲染、用户体验
- 结合常见优化建se建议
2. Timeline / Performance 面板
- 录制页面操作过程
- 分析帧率、Script 耗时、Rendering 耗时
3. 真机调试
- 阴影模式测试(模拟低配置设备)
- 强制缓慢网络测试
6. 资深面试题
- 小程序首屏渲染优化思路?
- 减少主包体积,提卸加载到分包
- 首屏数据在 onLoad 中请求并打开下一页齐平行发起
- 马鼓属屏 + 骨架屏 + 首屏内容尽早请求
- 首屏外的图片延迟加载
- setData 渲染 1000 条数据如何优化?
- 虚拟列表组件(只渲染可视区域)
- 分屏渲染,每帧只 setData 一小批
- 尸体数据尽量小,只存 key 字段而非全量对象
- 小程序页面切换数据如何保存?
- 小程序没有路由層缓存,页面切换后重新创建
- 解决:app.globalData / Storage / 页面层参数传递
- Taro/uni-app 可配置页面缓存 (keepAlive)