全栈手账 订阅 RSS

UniApp 开发小程序知识总结与面试题

UniApp 跨端开发的系统总结,涵盖目录与文件结构、页面与组件生命周期、pages.json 配置、条件编译、多端适配与性能优化,一套代码编译到小程序、H5 与 App。

读了 29 分钟 更新于 8 月 5 日

一、UniApp 基础概念

1. 什么是 UniApp?

  • 一个使用 Vue.js 开发跨平台应用的前端框架
  • 一套代码,可发布到 iOS、Android、H5、微信小程序、支付宝小程序等多个平台
  • 基于 Vue 2/3,兼容 npm 生态

2. UniApp 架构特点

  • 跨平台能力:一次开发,多端运行
  • 原生体验:通过原生渲染引擎实现接近原生的性能
  • Vue 语法:完全支持 Vue 2/3 的语法和生命周期
  • 小程序生态:支持微信、支付宝、百度等多个小程序平台

3. 与原生小程序的区别

维度 UniApp 原生小程序
技术栈 Vue + TypeScript WXML + WXSS + JS
跨平台 支持多端 仅微信小程序
开发体验 类 Vue 开发 小程序特有语法
生态 npm 生态 小程序生态

二、核心文件结构

code
├── src/│   ├── App.vue          # 根组件│   ├── main.ts          # 入口文件│   ├── pages.json       # 页面路由配置│   ├── manifest.json    # 应用配置│   ├── pages/           # 页面目录│   │   ├── index/│   │   │   └── index.vue│   └── components/      # 组件目录├── package.json└── vite.config.ts       # Vite 配置

三、生命周期详解

1. 应用生命周期

typescript
export default {  onLaunch() {    // 应用启动时触发  },  onShow() {    // 应用显示时触发  },  onHide() {    // 应用隐藏时触发  }}

2. 页面生命周期

typescript
export default {  onLoad(options) {    // 页面加载时触发  },  onShow() {    // 页面显示时触发  },  onReady() {    // 页面初次渲染完成  },  onHide() {    // 页面隐藏时触发  },  onUnload() {    // 页面卸载时触发  },  onPullDownRefresh() {    // 下拉刷新触发  },  onReachBottom() {    // 上拉触底触发  }}

3. 组件生命周期(Vue 标准)

  • beforeCreate → created → beforeMount → mounted
  • beforeUpdate → updated
  • beforeUnmount → unmounted

四、常用 API

1. 网络请求

typescript
// uni.request 封装const request = (options: UniApp.RequestOptions) => {  return new Promise((resolve, reject) => {    uni.request({      ...options,      success: (res) => resolve(res),      fail: (err) => reject(err)    })  })}// 使用示例const data = await request({  url: 'https://api.example.com/data',  method: 'GET'})

2. 页面路由

API 说明
uni.navigateTo 保留当前页面,跳转到新页面
uni.redirectTo 关闭当前页面,跳转到新页面
uni.switchTab 跳转到 tabBar 页面
uni.navigateBack 返回上一页
uni.reLaunch 关闭所有页面,打开新页面

3. 数据存储

typescript
// 存储uni.setStorageSync('user', { name: 'test' })// 读取const user = uni.getStorageSync('user')// 删除uni.removeStorageSync('user')

4. 界面交互

typescript
// 显示 Toastuni.showToast({  title: '成功',  icon: 'success'})// 显示加载框uni.showLoading({  title: '加载中...'})// 显示模态框uni.showModal({  title: '提示',  content: '确定删除?',  success: (res) => {    if (res.confirm) {      // 用户点击确定    }  }})

五、组件化开发

1. 创建组件

vue
<!-- components/MyComponent.vue --><template>  <view class="my-component">    <text>{{ title }}</text>    <button @click="handleClick">点击</button>  </view></template><script setup lang="ts">import { defineProps, defineEmits } from 'vue'const props = defineProps<{  title: string}>()const emit = defineEmits<{  (e: 'click', value: string): void}>()const handleClick = () => {  emit('click', 'hello')}</script>

2. 使用组件

vue
<template>  <MyComponent title="标题" @click="onClick" /></template><script setup lang="ts">import MyComponent from '@/components/MyComponent.vue'const onClick = (value: string) => {  console.log(value)}</script>

六、pages.json 配置详解

1. 基本配置

json
{  "pages": [    {      "path": "pages/index/index",      "style": {        "navigationBarTitleText": "首页",        "navigationStyle": "custom"      }    }  ],  "globalStyle": {    "navigationBarTextStyle": "black",    "navigationBarTitleText": "UniApp",    "navigationBarBackgroundColor": "#ffffff",    "backgroundColor": "#f5f5f5"  },  "tabBar": {    "color": "#999999",    "selectedColor": "#007aff",    "list": [      {        "pagePath": "pages/index/index",        "text": "首页",        "iconPath": "static/tabs/home.png",        "selectedIconPath": "static/tabs/home-active.png"      }    ]  }}

七、条件编译

1. 平台标识

标识 平台
H5 H5
MP-WEIXIN 微信小程序
MP-ALIPAY 支付宝小程序
APP-PLUS App

2. 使用示例

typescript
// #ifdef H5console.log('H5 平台')// #endif// #ifndef MP-WEIXINconsole.log('非微信小程序')// #endif
vue
<!-- #ifdef H5 --><view>H5 专属内容</view><!-- #endif -->

八、性能优化

1. 分包加载

json
{  "pages": [    { "path": "pages/index/index" }  ],  "subpackages": [    {      "root": "pages/subpackage",      "pages": ["page1", "page2"]    }  ]}

2. 图片优化

  • 使用合适尺寸的图片
  • 开启懒加载:image 组件添加 lazy-load 属性
  • 使用 WebP 格式

3. 代码优化

  • 避免频繁调用 uni.setData
  • 使用 computed 和 watch 优化数据响应
  • 合理使用组件缓存

4. 首屏优化

  • 减少首屏资源请求
  • 使用骨架屏
  • 异步加载非首屏数据

九、常见面试题

基础概念

Q1:什么是 UniApp?它的优势是什么?

  • 基于 Vue.js 的跨平台框架
  • 一套代码多端运行(小程序、H5、App)
  • 原生渲染,性能接近原生
  • 支持 npm 生态,开发体验好

Q2:UniApp 和原生小程序的区别?

  • 技术栈:UniApp 使用 Vue,原生使用 WXML/WXSS
  • 跨平台:UniApp 支持多端,原生仅支持微信
  • 开发效率:UniApp 更高,复用性强

Q3:UniApp 的生命周期有哪些?

  • 应用生命周期:onLaunch、onShow、onHide
  • 页面生命周期:onLoad、onShow、onReady、onHide、onUnload
  • 组件生命周期:Vue 标准生命周期

进阶问题

Q4:UniApp 如何实现跨平台兼容?

  • 通过条件编译区分不同平台
  • 使用统一的 API 层封装不同平台的实现
  • 编译时根据目标平台生成对应代码

Q5:如何在 UniApp 中使用 TypeScript?

  • 创建项目时选择 TypeScript 模板
  • 在 tsconfig.json 中配置路径别名
  • 使用 .vue 文件时添加 lang="ts"

Q6:UniApp 的路由跳转方式有哪些?

  • navigateTo:保留当前页面
  • redirectTo:关闭当前页面
  • switchTab:跳转到 tabBar 页面
  • reLaunch:关闭所有页面

性能优化

Q7:如何优化 UniApp 小程序的性能?

  • 分包加载,减小主包体积
  • 优化图片,使用懒加载
  • 减少 setData 调用次数和数据量
  • 使用骨架屏提升首屏体验

Q8:UniApp 小程序的包体积限制是多少?如何优化?

  • 主包:2MB,分包:20MB
  • 优化:代码压缩、图片优化、分包加载、删除无用代码

实战经验

Q9:如何处理 UniApp 中的跨域问题?

  • 开发环境:配置 h5 端的 devServer.proxy
  • 小程序:在后台配置合法域名白名单

Q10:UniApp 如何调用原生能力?

  • 使用官方提供的 API(如 uni.chooseImage)
  • 通过 uni.requireNativePlugin 调用原生插件
  • 使用 App 端的 Native.js

原理与架构

Q11:UniApp 的编译原理是什么?如何将 Vue 代码转换为小程序代码?

编译流程:

  1. 代码解析:解析 .vue 文件,提取 template、script、style
  2. AST 转换:将 Vue 的 AST 转换为小程序的 AST
  3. 代码生成:根据目标平台生成对应的代码
    • 微信小程序:生成 .wxml、.wxss、.js、.json
    • H5:生成标准 HTML/CSS/JS
  4. 资源处理:处理图片、字体等静态资源

核心转换逻辑:

  • v-if → wx:if
  • v-for → wx:for
  • @click → bindtap
  • :class → class(动态绑定)

编译器核心代码:

javascript
// 简化的标签转换逻辑const tagMap = {  'view': 'view',  'text': 'text',  'image': 'image',  'button': 'button'}function transformTag(tag) {  return tagMap[tag] || 'view'}// 属性转换const attrMap = {  ':class': 'class',  ':style': 'style',  '@click': 'bindtap',  '@input': 'bindinput'}

Q12:UniApp 的渲染原理是什么?为什么能实现接近原生的性能?

渲染架构:

  1. 逻辑层:运行在 JavaScript 引擎中,处理业务逻辑
  2. 视图层:运行在原生渲染引擎中,负责 UI 渲染
  3. 通信机制:逻辑层和视图层通过桥接层通信

性能优化手段:

  • 原生组件渲染:使用原生组件而非 WebView 渲染
  • 双线程分离:逻辑和渲染分离,避免阻塞
  • 批量更新:合并多次 setData 调用,减少通信开销
  • 缓存策略:组件缓存、图片缓存、页面缓存

setData 优化示例:

typescript
// 不好的写法:多次调用this.setData({ list: newList1 })this.setData({ count: newCount })// 好的写法:合并调用this.setData({  list: newList1,  count: newCount})

高级组件开发

Q13:如何在 UniApp 中封装一个高性能的列表组件?需要注意哪些问题?

实现要点:

vue
<template>  <scroll-view     scroll-y     @scrolltolower="loadMore"    :scroll-top="scrollTop"  >    <view v-for="(item, index) in list" :key="item.id">      <slot :item="item" :index="index" />    </view>    <view v-if="loading" class="loading">加载中...</view>    <view v-if="noMore" class="no-more">没有更多了</view>  </scroll-view></template><script setup lang="ts">import { ref, computed } from 'vue'const props = defineProps<{  list: any[]  loading: boolean  noMore: boolean}>()const emit = defineEmits<{  (e: 'loadMore'): void}>()const scrollTop = ref(0)const loadMore = () => {  if (!props.loading && !props.noMore) {    emit('loadMore')  }}</script>

注意事项:

  • key 的使用:必须使用唯一的 key,避免列表错乱
  • 避免复杂计算:列表项中避免使用复杂的 computed 属性
  • 图片懒加载:使用 lazy-load 属性
  • 虚拟化:长列表(>1000条)考虑使用虚拟列表

Q14:如何实现 UniApp 组件的自定义事件和双向绑定?

自定义事件:

vue
<!-- 子组件 --><script setup lang="ts">const emit = defineEmits<{  (e: 'update:modelValue', value: string): void  (e: 'change', value: string): void}>()const handleInput = (e: Event) => {  const value = (e.target as HTMLInputElement).value  emit('update:modelValue', value)  emit('change', value)}</script>

双向绑定:

vue
<!-- 父组件 --><template>  <CustomInput v-model="inputValue" @change="onChange" /></template><script setup lang="ts">import { ref } from 'vue'import CustomInput from '@/components/CustomInput.vue'const inputValue = ref('')const onChange = (value: string) => {  console.log('值改变:', value)}</script>

跨平台兼容

Q15:UniApp 中不同平台的 API 差异如何处理?有哪些兼容性方案?

方案一:条件编译

typescript
// #ifdef MP-WEIXINuni.setNavigationBarColor({  frontColor: '#ffffff',  backgroundColor: '#007aff'})// #endif// #ifdef H5document.querySelector('title')!.textContent = '新标题'// #endif

方案二:统一封装

typescript
export const setTitle = (title: string) => {  // #ifdef MP-WEIXIN  uni.setNavigationBarTitle({ title })  // #endif    // #ifdef H5  document.title = title  // #endif}

方案三:API 能力检测

typescript
const shareData = {  title: '分享标题',  path: '/pages/index/index'}if (uni.share) {  uni.share(shareData)} else {  uni.showToast({ title: '当前平台不支持分享', icon: 'none' })}

常见平台差异:

平台 差异点
微信小程序 支付、分享、登录 API 特有
支付宝小程序 芝麻信用、生活号 API 特有
H5 支持 DOM API,有跨域问题
App 支持 Native.js、原生插件

状态管理

Q16:UniApp 中如何设计状态管理?Pinia 和 Vuex 在小程序中的使用有什么注意事项?

Pinia 在 UniApp 中的配置:

typescript
// stores/user.tsimport { defineStore } from 'pinia'import { ref, computed } from 'vue'export const useUserStore = defineStore('user', () => {  const userInfo = ref(null)  const token = ref('')    const isLoggedIn = computed(() => !!token.value)    const login = (data: { user: any; token: string }) => {    userInfo.value = data.user    token.value = data.token    uni.setStorageSync('token', data.token)  }    const logout = () => {    userInfo.value = null    token.value = ''    uni.removeStorageSync('token')  }    return { userInfo, token, isLoggedIn, login, logout }})

使用注意事项:

  • 持久化:小程序端需手动处理 localStorage,H5 可使用 pinia-plugin-persistedstate
  • SSR 兼容性:小程序端无服务端渲染,无需考虑
  • 性能:避免在 store 中存储过大的数据
  • 模块化:按业务模块拆分 store

在组件中使用:

vue
<script setup lang="ts">import { useUserStore } from '@/stores/user'const userStore = useUserStore()const handleLogin = () => {  userStore.login({ user: { name: 'test' }, token: 'xxx' })}</script>

微信云开发

Q17:如何在 UniApp 中集成微信云开发?云函数和云数据库的使用场景是什么?

初始化云开发:

typescript
// App.vueonLaunch() {  // #ifdef MP-WEIXIN  wx.cloud.init({    env: 'your-env-id',    traceUser: true  })  // #endif}

云数据库操作:

typescript
// 获取数据库实例const db = wx.cloud.database()// 查询数据const queryData = async () => {  const res = await db.collection('users')    .where({ status: 1 })    .orderBy('createTime', 'desc')    .limit(10)    .get()  return res.data}// 添加数据const addData = async (data: any) => {  const res = await db.collection('users').add({ data })  return res._id}// 更新数据const updateData = async (id: string, data: any) => {  await db.collection('users').doc(id).update({ data })}

云函数调用:

typescript
// 调用云函数const callCloudFunction = async (name: string, data?: any) => {  const res = await wx.cloud.callFunction({ name, data })  return res.result}// 使用示例const login = async () => {  const res = await callCloudFunction('login', { code: 'xxx' })  return res}

适用场景:

  • 云函数:处理复杂业务逻辑、敏感操作(如支付、权限验证)
  • 云数据库:存储和管理业务数据
  • 云存储:上传和管理图片、文件等静态资源

性能优化进阶

Q18:小程序的性能指标有哪些?如何进行性能监控和优化?

核心性能指标:

指标 含义 优化目标
首次渲染时间 页面从加载到首次渲染完成的时间 < 1.5s
首次内容绘制 页面首个内容像素渲染的时间 < 1s
最大绘制时间 页面最大渲染帧的耗时 < 16ms
JS 执行时间 JavaScript 代码执行耗时 < 1s
内存占用 小程序运行时的内存使用 < 256MB

性能监控方案:

typescript
// 使用 performance APIconst start = performance.now()// 执行代码await fetchData()const end = performance.now()console.log('数据加载耗时:', end - start)// 监控内存const memoryInfo = wx.getMemoryInfo?.()if (memoryInfo) {  console.log('内存占用:', memoryInfo.usedJSHeapSize)}

优化策略:

  1. 代码分割:按需加载非核心代码
  2. 懒加载:图片、组件按需渲染
  3. 缓存策略:数据缓存、页面缓存
  4. 减少通信:合并 setData、减少跨线程通信
  5. 优化算法:使用更高效的数据结构和算法

Q19:如何优化 UniApp 小程序的首屏加载速度?

首屏优化方案:

typescript
// 1. 预加载关键数据onLaunch() {  // 提前加载用户信息  this.preloadUserInfo()}// 2. 骨架屏<template>  <view v-if="loading" class="skeleton">    <view class="skeleton-item"></view>    <view class="skeleton-item"></view>  </view>  <view v-else>    <!-- 真实内容 -->  </view></template>// 3. 异步加载import { defineAsyncComponent } from 'vue'const HeavyComponent = defineAsyncComponent(() =>   import('./HeavyComponent.vue'))

优化措施:

措施 效果
分包加载 减小主包体积,加快首屏加载
资源压缩 压缩 JS/CSS/图片
懒加载组件 非首屏组件按需加载
数据预取 在 onLaunch 中提前请求数据
骨架屏 提升用户感知体验

实战高级问题

Q20:如何在 UniApp 中实现微信支付和分享功能?需要注意哪些安全问题?

微信支付实现:

typescript
const pay = async (orderId: string) => {  try {    // 1. 后端生成支付参数    const payParams = await request({      url: '/api/pay/create',      method: 'POST',      data: { orderId }    })        // 2. 调用微信支付    const res = await uni.requestPayment({      provider: 'wxpay',      timeStamp: payParams.timeStamp,      nonceStr: payParams.nonceStr,      package: payParams.package,      signType: 'MD5',      paySign: payParams.paySign    })        if (res.errMsg === 'requestPayment:ok') {      uni.showToast({ title: '支付成功', icon: 'success' })    }  } catch (e) {    uni.showToast({ title: '支付失败', icon: 'none' })  }}

微信分享实现:

typescript
onLoad() {  // #ifdef MP-WEIXIN  wx.showShareMenu({    withShareTicket: true,    menus: ['shareAppMessage', 'shareTimeline']  })    wx.onShareAppMessage(() => ({    title: '分享标题',    path: '/pages/index/index',    imageUrl: '/static/share.png'  }))  // #endif}

安全注意事项:

  • 支付签名:签名必须在服务端生成,禁止客户端生成
  • 订单校验:支付成功后必须验证订单状态
  • 防重放攻击:使用 nonce 和 timestamp 防止重复请求
  • 数据加密:敏感数据传输使用 HTTPS
  • 权限控制:分享内容需符合平台规范

十、注意事项

  1. 平台差异:不同平台的 API 支持程度不同,需做好兼容
  2. 性能监控:关注小程序的性能指标,及时优化
  3. 调试工具:使用 HBuilderX 和微信开发者工具进行调试
  4. 发布流程:不同平台的发布流程不同,需分别配置

十一、总结

UniApp 是一个强大的跨平台开发框架,通过 Vue 语法实现一次开发多端运行。掌握其核心概念、生命周期、API 使用和性能优化技巧,是成为优秀 UniApp 开发者的关键。

前端、后端、部署、客服都是我一个人。写这里的东西都是真踩过的坑,不是抄来的教程。