uni-app 面试知识点梳理
-
- 一、基础认知(必问)
-
- [1.1 一句话定义](#1.1 一句话定义)
- [1.2 跨端原理(高频)](#1.2 跨端原理(高频))
- [1.3 uni-app 与 Vue 的关系](#1.3 uni-app 与 Vue 的关系)
- [1.4 Vue2 与 Vue3 选型(面试加分点)](#1.4 Vue2 与 Vue3 选型(面试加分点))
- 二、项目结构与配置
-
- [2.1 标准目录](#2.1 标准目录)
- [2.2 pages.json(核心配置,必会)](#2.2 pages.json(核心配置,必会))
- [2.3 标签映射(易踩坑)](#2.3 标签映射(易踩坑))
- 三、生命周期(高频重灾区)
-
- [3.1 三层生命周期](#3.1 三层生命周期)
- [3.2 页面 vs 组件生命周期执行顺序(经典题)](#3.2 页面 vs 组件生命周期执行顺序(经典题))
- [3.3 onLoad 与 onShow 区别(必背)](#3.3 onLoad 与 onShow 区别(必背))
- 四、路由与跳转
-
- [4.1 五种跳转 API](#4.1 五种跳转 API)
- [4.2 传参与接收](#4.2 传参与接收)
- [4.3 路由拦截](#4.3 路由拦截)
- 五、网络请求与封装(实战必考)
-
- [5.1 原生请求](#5.1 原生请求)
- [5.2 面试级封装要点](#5.2 面试级封装要点)
- [5.3 文件上传/下载](#5.3 文件上传/下载)
- [六、状态管理(Vuex / Pinia)](#六、状态管理(Vuex / Pinia))
-
- [6.1 何时需要](#6.1 何时需要)
- [6.2 Vuex 经典结构](#6.2 Vuex 经典结构)
- [6.3 Pinia(Vue3 推荐)](#6.3 Pinia(Vue3 推荐))
- [6.4 跨页传值替代方案](#6.4 跨页传值替代方案)
- [七、条件编译(uni-app 特色,必考)](#七、条件编译(uni-app 特色,必考))
-
- [7.1 语法](#7.1 语法)
- [7.2 各位置写法](#7.2 各位置写法)
- [7.3 常用平台标识](#7.3 常用平台标识)
- 八、小程序专项(结合实际项目重点)
-
- [8.1 登录流程(微信小程序必考)](#8.1 登录流程(微信小程序必考))
- [8.2 分包加载(性能关键)](#8.2 分包加载(性能关键))
- [8.3 rpx 单位](#8.3 rpx 单位)
- [8.4 微信支付](#8.4 微信支付)
- [九、组件通信(Vue 通用 + uni 特有)](#九、组件通信(Vue 通用 + uni 特有))
-
- [9.1 通信方式对比](#9.1 通信方式对比)
- [9.2 组件生命周期在 uni 中的注意点](#9.2 组件生命周期在 uni 中的注意点)
- 十、性能优化(高频)
- 十一、易踩坑清单(实战血泪)
- [十二、uni-app X 与新趋势(加分)](#十二、uni-app X 与新趋势(加分))
- [十三、高频面试题 30 连击(自测)](#十三、高频面试题 30 连击(自测))
- [附:答题模板(STAR 简化版)](#附:答题模板(STAR 简化版))
定位:求职技术岗(uni-app / 小程序前端)面试速通。
范围:以微信小程序 为主战场,兼顾 H5 / App。Vue2 + Vue3 双栈覆盖。
用法:每个模块先看"核心结论",再看"面试题",最后对照"易踩坑"。
一、基础认知(必问)
1.1 一句话定义
uni-app 是 DCloud 出品的、基于 Vue 语法的跨端应用框架,一套代码可编译到微信/支付宝/百度/抖音小程序、H5、App(iOS/Android)、快应用等。
1.2 跨端原理(高频)
| 层 | 作用 | 说明 |
|---|---|---|
| 编译器(cli/ide) | 把 Vue SFC 编译成各平台目标代码 | 小程序:.vue → wxml/wxss/js/json;H5:标准 Vue;App:weex/uts |
| 运行时(runtime) | 抹平各平台 API 差异 | uni.xxx 调用在底层映射到 wx.xxx / navigator / plus |
| 条件编译 | 差异化代码 | #ifdef MP-WEIXIN ... #endif |
关键结论 :uni-app = Vue 语法 + 平台编译器 + 统一运行时。它不是"套壳 webview",小程序端是原生组件渲染。
1.3 uni-app 与 Vue 的关系
- 模板、数据绑定、组件、生命周期沿用了 Vue。
- 小程序平台下没有 DOM/BOM ,
document、window、localStorage在小程序端不存在 → 必须用uni.xxx替代。 - Vue3 版本使用 Vite 编译,性能与 Tree-shaking 更优;Vue2 版本逐步进入维护态。
1.4 Vue2 与 Vue3 选型(面试加分点)
| 维度 | Vue2(Options) | Vue3(Composition + Vite) |
|---|---|---|
| 编译器 | vue-cli / HbuilderX | Vite(更快) |
| 响应式 | Object.defineProperty |
Proxy |
| 写法 | data(){return{}} / methods |
setup() / <script setup> |
| 状态管理 | Vuex | Pinia(官方推荐) |
| 兼容性 | 老项目多 | 新项目首选 |
二、项目结构与配置
2.1 标准目录
├─ pages/ # 页面(每个页一个文件夹)
│ ├─ index/index.vue
├─ components/ # 自定义组件
├─ static/ # 静态资源(图片等,不被编译)
├─ store/ # Vuex/Pinia
├─ utils/ # 工具(request 封装等)
├─ App.vue # 应用入口(应用生命周期)
├─ main.js # 入口脚本
├─ pages.json # ★ 路由 + 全局样式 + 分包配置
├─ manifest.json # 应用配置(appid、名称、图标)
└─ uni.scss # 全局样式变量
2.2 pages.json(核心配置,必会)
json
{
"pages": [
{ "path": "pages/index/index", "style": { "navigationBarTitleText": "首页" } }
],
"globalStyle": { "navigationBarBackgroundColor": "#007AFF" },
"tabBar": {
"list": [
{ "pagePath": "pages/index/index", "text": "首页", "iconPath": "..." }
]
},
"subPackages": [
{ "root": "packageA", "pages": [{ "path": "list/list" }] }
],
"preloadRule": {
"pages/index/index": { "network": "all", "packages": ["packageA"] }
}
}
面试题 :pages 数组第一项是什么?→ 应用启动页(首页)。
2.3 标签映射(易踩坑)
| HTML | uni-app |
|---|---|
<div> |
<view> |
<span> <a> |
<text> |
<img> |
<image> |
<input> |
<input> / <textarea> |
<ul><li> |
无,用 <view> 模拟 |
注意:
<text>内只能嵌套文本/文本组件,放<view>不生效。
三、生命周期(高频重灾区)
3.1 三层生命周期
| 层级 | 钩子 | 触发时机 |
|---|---|---|
| 应用级(App.vue) | onLaunch |
应用初始化(全局一次) |
| 应用级 | onShow / onHide |
应用前台/后台 |
| 页面级 | onLoad(options) |
页面加载(传参在这里接收) |
| 页面级 | onShow / onReady / onHide / onUnload |
显示/渲染完成/隐藏/卸载 |
| 组件级(Vue2) | created/mounted 等 |
同 Vue |
| 组件级(Vue3) | onMounted 等(需 import) |
组合式 |
3.2 页面 vs 组件生命周期执行顺序(经典题)
页面 onLoad → 组件 created → 组件 mounted → 页面 onReady
- 页面
onLoad早于组件mounted。 - 页面切走:
onHide;再回来:onShow(组件不重新创建,故created不重复)。
3.3 onLoad 与 onShow 区别(必背)
onLoad:页面创建时 执行一次,适合初始化、options取参。onShow:每次显示都执行,适合刷新数据(如从详情页返回列表需刷新)。
四、路由与跳转
4.1 五种跳转 API
| API | 表现 | 是否可返回 | 栈上限 |
|---|---|---|---|
navigateTo |
打开新页 | 是 | 10 层 |
redirectTo |
替换当前页 | 否 | --- |
switchTab |
跳 tabBar 页 | 否 | --- |
reLaunch |
关闭所有重开 | 否 | --- |
navigateBack |
返回 | --- | --- |
易踩坑:
switchTab不能传参(URL 参数会被忽略),跨 tab 传值要用全局状态/本地缓存。navigateTo超过 10 层会失败,需redirectTo或reLaunch兜底。
4.2 传参与接收
js
// A 页
uni.navigateTo({ url: '/pages/detail/detail?id=123&name=test' })
// B 页 onLoad
onLoad(options) {
console.log(options.id, options.name) // '123' 'test'(均为字符串)
}
接收值永远是字符串 ,数字需
Number()转换。
4.3 路由拦截
- 全局:在
App.vue的onShow中根据uni.getLaunchOptionsSync()/ 页面栈做权限判断。 - 推荐用
uni.addInterceptor('navigateTo', {...})做统一登录校验(实战常用)。
五、网络请求与封装(实战必考)
5.1 原生请求
js
uni.request({
url: 'https://api.example.com/list',
method: 'GET',
data: { page: 1 },
header: { 'Authorization': token },
success: (res) => {},
fail: (err) => {}
})
5.2 面试级封装要点
- Promise 化 :用
return new Promise包裹,支持async/await。 - 拦截器:请求前注入 token,响应后统一处理 401/状态码。
- 错误统一处理:网络错误、业务错误码分层。
- baseURL 环境切换:dev / prod 通过条件编译或 env 区分。
js
// utils/request.js 简化版
const BASE_URL = 'https://api.example.com'
export function request(options) {
return new Promise((resolve, reject) => {
uni.request({
url: BASE_URL + options.url,
method: options.method || 'GET',
data: options.data,
header: { 'Authorization': uni.getStorageSync('token') || '' },
success: (res) => {
if (res.statusCode === 200 && res.data.code === 0) resolve(res.data)
else if (res.data.code === 401) { uni.navigateTo({url:'/pages/login/login'}); reject(res.data) }
else reject(res.data)
},
fail: reject
})
})
}
5.3 文件上传/下载
- 上传:
uni.uploadFile({ url, filePath, name:'file', formData }) - 下载:
uni.downloadFile→ 拿到临时路径 →uni.saveFile - 小程序端不能直接操作本地文件系统路径,只能拿到临时路径。
六、状态管理(Vuex / Pinia)
6.1 何时需要
- 跨页面共享(用户信息、登录态、购物车)。
- 全局配置(主题、地区)。
6.2 Vuex 经典结构
js
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: { token: '' },
mutations: { setToken(s, v){ s.token = v } },
actions: { login({commit}, payload){ /* 异步 */ commit('setToken', payload) } },
getters: { isLogin: s => !!s.token }
})
6.3 Pinia(Vue3 推荐)
- 无
mutations,state/actions/getters更直接。 - 天然支持 Composition API,
storeToRefs解构保持响应性。
6.4 跨页传值替代方案
| 场景 | 方案 |
|---|---|
| 兄弟/跨多级页 | Vuex/Pinia |
| 简单临时 | uni.setStorageSync |
| 页面栈直取 | getCurrentPages() 取上一页 setData |
七、条件编译(uni-app 特色,必考)
7.1 语法
js
// #ifdef MP-WEIXIN
console.log('只在微信小程序编译')
// #endif
// #ifndef H5
console.log('除了 H5 都编译')
// #endif
// 多平台
// #ifdef MP-WEIXIN || MP-ALIPAY
7.2 各位置写法
| 位置 | 写法 |
|---|---|
| 模板 | <!-- #ifdef MP-WEIXIN --> |
| 样式 | /* #ifdef MP-WEIXIN */ |
| 脚本 | // #ifdef MP-WEIXIN |
7.3 常用平台标识
MP-WEIXIN MP-ALIPAY MP-BAIDU MP-TOUTIAO H5 APP-PLUS APP MP(所有小程序)。
八、小程序专项(结合实际项目重点)
8.1 登录流程(微信小程序必考)
1. 前端 uni.login() → 拿到 code
2. 把 code 发给后端
3. 后端用 code + appsecret 调微信接口换 openid/session_key
4. 后端生成自定义 token 返回前端
5. 前端存 token,后续请求携带
注意:code 只能用小程序的
wx.login/uni.login换,不能前端直接拿 openid(安全)。
8.2 分包加载(性能关键)
- 主包过大(微信限制 2MB 主包)会导致无法预览/上传 → 必须分包。
- 分包通过
subPackages配置,独立打包,使用时按需下载。 - 分包预下载
preloadRule优化首跳体验。
8.3 rpx 单位
750rpx = 屏幕宽度,自适应各机型。- 设计稿通常按 750px 出图,1:1 写 rpx。
- 注意:rpx 有个别机型 rounding 误差,关键尺寸可用 px 兜底。
8.4 微信支付
- 前端:
uni.requestPayment({ provider:'wxpay', timeStamp, nonceStr, package, signType, paySign }) - 参数由后端统一下单生成,前端只负责调起。
九、组件通信(Vue 通用 + uni 特有)
9.1 通信方式对比
| 方式 | 适用 | 说明 |
|---|---|---|
props / $emit |
父子 | 标准 |
provide / inject |
跨层 | Vue 原生 |
uni.$on / uni.$emit |
任意组件 | uni 事件总线 ,需 uni.$off 销毁防内存泄漏 |
| Vuex/Pinia | 全局 | 状态 |
ref |
父调子方法 | this.$refs.child.fn() |
9.2 组件生命周期在 uni 中的注意点
- 小程序组件无
onShow,只有 Vue 的created/mounted等。 - 需监听页面显示时,用
uni.$on('pageShow')在页面onShow中触发。
十、性能优化(高频)
- 分包:控制主包 < 2MB。
- 图片 :用 CDN + 压缩,避免本地大图;
lazy-load懒加载。 - 长列表 :数据量大用
uni.createIntersectionObserver或虚拟列表,避免一次性渲染。 - v-for 必须 key:用稳定 id,不用 index(小程序重排 bug)。
- 减少 setData 频率/体积:小程序 setData 有性能成本,合并数据、避免传大对象。
- onLoad 初始化 / onShow 轻量刷新 :别在
onShow做重活。 - 组件按需引入:非全局组件局部注册。
十一、易踩坑清单(实战血泪)
| 坑 | 现象 | 解决 |
|---|---|---|
| 小程序无 window/document | 报错 undefined | 用 uni API 替代 |
| switchTab 传参无效 | 收不到 | 用 storage/store |
| 页面栈超 10 层 | navigateTo 失败 | redirectTo/reLaunch |
| rpx 误差 | 个别机型错位 | 关键尺寸 px |
| 事件总线未 off | 重复触发 | 页面 onUnload 中 $off |
| 本地图片过大 | 主包超限 | 转 CDN / 分包 |
| H5 端跨域 | 请求失败 | 配 proxy 或后端 CORS |
| 真机与模拟器差异 | 样式/API 不一致 | 必须真机调试 |
| v-if 与 v-for 同元素 | 警告/逻辑错 | 拆开或用 computed 过滤 |
| 自定义组件样式隔离 | 外部样式不生效 | options:{styleIsolation:'shared'} 或 ::v-deep |
十二、uni-app X 与新趋势(加分)
- uni-app x :用
uts(TypeScript 风格)直接编译为原生(Kotlin/Swift),性能接近原生,但生态尚在完善。 - Vue3 + Vite 已成新项目默认。
- uniCloud:DCloud 的 serverless,前后端一体。
- 面试时如能对比"传统编译型 vs uni-app x 原生型"体现技术视野。
十三、高频面试题 30 连击(自测)
- uni-app 跨端原理是什么?
- Vue2 和 Vue3 在 uni-app 中的区别?
- 页面生命周期与组件生命周期执行顺序?
- onLoad 和 onShow 的区别与适用场景?
- 小程序为什么没有 DOM?如何替代?
- 五种路由 API 及使用区别?
- switchTab 为什么不能传参?怎么解决?
- 页面栈上限是多少?超限怎么办?
- 如何封装 uni.request?
- 401 怎么统一处理?
- 微信小程序登录流程?
- code 能在前端直接换 openid 吗?为什么?
- 什么是分包?为什么要分包?主包限制?
- 分包预下载有什么用?
- rpx 是什么?设计稿怎么对应?
- 条件编译语法?常用平台标识?
- 如何在 H5 和小程序写不同代码?
- Vuex 和 Pinia 区别?uni 推荐哪个?
- 跨页面传值有哪些方式?
- uni.$on 需要注意什么?
- 父子组件通信方式?
- provide/inject 适用场景?
- 小程序支付流程?谁生成签名?
- 如何做路由拦截/登录校验?
- 长列表如何优化?
- setData 为什么有性能成本?如何优化?
- v-for 为什么必须 key?能用 index 吗?
- 真机调试为什么必要?
- 事件总线内存泄漏怎么避免?
- uni-app x 相比传统方案优势?
附:答题模板(STAR 简化版)
被问原理类 :先一句话结论 → 再讲分层/流程 → 举项目中的例子。
被问优化类:先说现象/指标 → 再给手段(分包/懒加载/合并 setData)→ 讲你项目里做到了哪一步。