人人都会AI编程

27.1 UniApp 跨端开发

更新时间:2026-07-09

移动互联网时代,产品通常需要同时覆盖 iOS、Android、微信小程序、H5 等多个端。如果每个端都独立开发,成本会成倍增长。UniApp 就是为了解决这个问题而生的——它让你用 一套 Vue 代码,编译输出到多个平台。

核心原理:一套代码,多端编译

UniApp 并不是把 H5 页面直接打包成 App,而是做了两件关键的事:

  1. 编译时转换:在构建阶段,UniApp 的编译器会把 Vue 模板、组件和 API 调用,根据目标平台进行代码转换和替换。例如,<view> 标签在小程序端会原样输出,在 App 端会被映射为原生视图组件,在 H5 端则被编译成 HTML 的 <div> 标签。
  2. 运行时适配层:UniApp 提供了一套统一的 API(如 uni.requestuni.navigateTouni.getSystemInfo),这些 API 在底层根据运行环境调用对应端的能力(小程序直接调用微信 API,App 端通过 JS 桥接原生模块,H5 端则用标准的浏览器 API)。

最终的效果是:你写的 Vue 文件,可以编译为微信小程序、支付宝小程序、字节跳动小程序、App(基于 Weex 或原生渲染)、H5,甚至快应用。虽然做不到 100% “完全无差异”,但在大部分常规业务场景中,代码复用率可以高达 80% 以上。

项目结构与开发体验

一个 UniApp 项目的目录结构对 Vue 开发者非常友好:

project/
├── pages/            # 页面文件,类似于 Vue 的路由页面
│   └── index/
│       └── index.vue
├── components/       # 通用 Vue 组件
├── static/           # 静态资源(不经过编译)
├── App.vue           # 应用入口
├── main.js           # 入口脚本
├── manifest.json     # 应用配置(App 包名、权限等)
├── pages.json        # 页面路由、窗口样式、tabBar 配置
└── uni.scss          # 全局样式变量

pages.json 作用类似于小程序原生的 app.json 和 Vue Router 的结合体,在这里配置路由、导航栏标题、底部 tab 等。Vue 组件仍然使用 <template><script><style> 三段式,支持组合式 API、响应式数据、生命周期钩子——这些你熟悉的 Vue 开发方式全部保留。

条件编译:一处代码适配多端差异

即使 UniApp 尽力抹平了平台差异,仍然有一些现实场景需要写平台特定代码(比如 iOS 需要请求相册权限、H5 不需要)。UniApp 提供了条件编译,通过特殊的注释标记来区分平台:

<template>
  <view>
    <!-- #ifdef MP-WEIXIN -->
    <button open-type="getUserInfo">微信授权登录</button>
    <!-- #endif -->
    
    <!-- #ifdef H5 -->
    <button @click="h5Login">普通登录</button>
    <!-- #endif -->
  </view>
</template>

<script setup>
// #ifdef APP-PLUS
const deviceInfo = plus.device.getInfo()
// #endif

// #ifndef H5
// 这段代码只在非 H5 平台编译
console.log('运行在小程序或 App 上')
// #endif
</script>

<style>
/* #ifdef MP-WEIXIN */
.title { color: #07c160; } /* 微信小程序专用绿色 */
/* #endif */
</style>

常用条件编译指令包括 #ifdef(如果定义了某平台)、#ifndef(如果未定义某平台)、#endif。编译时,不满足条件的代码会被直接移除,不影响包体积。这种方式比运行时判断 if (process.env) 更干净,也不会把无关代码打包进去。

原生能力调用:统一的 uni API

UniApp 封装了大量原生能力,通过 uni.xxx 系列 API 直接调用,无需关心底层实现:

| 功能 | 调用示例 | 说明 |
|------|---------|------|
| 网络请求 | uni.request({ url: '/api/data' }) | 跨平台 HTTP 请求,支持 Promise |
| 页面跳转 | uni.navigateTo({ url: '/pages/detail' }) | 与 Vue Router 用法类似 |
| 数据存储 | uni.setStorageSync('key', 'value') | 简单的本地持久化存储 |
| 获取定位 | uni.getLocation({ type: 'gcj02' }) | 返回经纬度,各端权限处理统一 |
| 拨打电话 | uni.makePhoneCall({ phoneNumber: '10086' }) | App/H5 皆可 |
| 选择图片 | uni.chooseImage({ count: 1 }) | 调用相册或拍照,返回临时路径 |
| 支付 | uni.requestPayment({ ... }) | 微信/支付宝支付,各端配置略有不同但调用统一 |

这些 API 在多数常用场景下都能直接跨端运行,只有当调用平台不支持的能力时(比如微信小程序的订阅消息在 App 端无法使用),需要在代码中做条件处理或提示用户。

组件生态与市场

UniApp 有一个官方维护的插件市场,提供了数千个可以直接引入的组件和模板,如滑动菜单、支付组件、地图轨迹、富文本解析器等。大多数组件遵循“引入即用”的原则,通过 pages.json 中的 easycom 配置,甚至可以直接使用无需手动 import

例如,使用官方 UIView 组件 uni-list

<uni-list>
  <uni-list-item title="标题" note="描述" />
</uni-list>

这些组件已经适配了多端差异,减少了开发者自己处理样式的重复工作。

现实中的定位与取舍

UniApp 不是银弹。在某些高性能场景(如复杂动画、大量实时数据的大屏应用)或需要深度平台特性的 App 上,原生开发仍然是更好的选择。但在占多数的信息展示、表单提交、列表详情类业务中,UniApp 能显著节省人力和维护成本。

一个常见的架构是:把核心业务逻辑放在 UniApp 项目中,对于需要强大原生能力的模块(如 AR、蓝牙通信),用原生插件进行扩展。UniApp 支持开发者自定义原生模块并通过 JS 桥接调用,这样既享受到跨端开发的效率,又不丢失原生能力。

总之,如果你的团队需要同时输出 Web、微信小程序和 App,并且技术栈已经是 Vue,UniApp 是当下最务实、门槛最低的跨端方案之一。它把“一次编写”变成了“一次编写,多处适配”,而不是“一次编写,多处修 bug”。