前端-UniApp基础知识

7 阅读5分钟

UniApp 基础知识


一、UniApp 概述

1.1 什么是 UniApp

UniApp 是一个使用 Vue.js 开发所有前端应用的框架,开发者编写一套代码,可发布到 iOS、Android、H5、以及各种小程序(微信/支付宝/百度/头条/QQ/快手/钉钉/淘宝)等多个平台。

核心特点:

  • 一套代码,多端运行
  • 基于 Vue.js
  • 丰富的组件和 API
  • 条件编译,平台差异化
  • 性能接近原生

1.2 支持的平台

平台说明
H5浏览器
小程序微信、支付宝、百度、头条、QQ、快手、钉钉、淘宝
AppiOS、Android
快应用华为、小米等

1.3 技术架构

┌─────────────────────────────────┐
│      UniApp 框架层              │
├─────────────────────────────────┤
│   Vue.js + 条件编译 + 运行时     │
├─────────────────────────────────┤
│   原生渲染层(各平台)            │
└─────────────────────────────────┘

二、快速开始

2.1 安装 HBuilderX

  1. 下载 HBuilderX:www.dcloud.io/hbuilderx.h…
  2. 安装插件:uni-app 编译插件
  3. 创建项目:文件 → 新建 → 项目 → uni-app

2.2 使用 CLI 创建项目

# 安装 Vue CLI
npm install -g @vue/cli

# 创建项目
vue create -p dcloudio/uni-preset-vue my-project

# 进入项目
cd my-project

# 运行到 H5
npm run dev:h5

# 运行到微信小程序
npm run dev:mp-weixin

# 运行到 App
npm run dev:app-plus

2.3 项目结构

my-project/
├── pages/              # 页面目录
│   ├── index/
│   │   └── index.vue
│   └── about/
│       └── about.vue
├── components/         # 组件目录
├── static/            # 静态资源
├── uni_modules/       # uni-app 插件
├── App.vue            # 应用入口
├── main.js            # 入口文件
├── manifest.json      # 应用配置
├── pages.json         # 页面路由配置
└── uni.scss           # 全局样式

三、页面配置

3.1 pages.json

{
  "pages": [
    {
      "path": "pages/index/index",
      "style": {
        "navigationBarTitleText": "首页",
        "enablePullDownRefresh": true,
        "navigationBarBackgroundColor": "#007AFF",
        "navigationBarTextStyle": "white"
      }
    },
    {
      "path": "pages/about/about",
      "style": {
        "navigationBarTitleText": "关于"
      }
    }
  ],
  "globalStyle": {
    "navigationBarTextStyle": "black",
    "navigationBarTitleText": "UniApp",
    "navigationBarBackgroundColor": "#F8F8F8",
    "backgroundColor": "#F8F8F8"
  },
  "tabBar": {
    "color": "#7A7E83",
    "selectedColor": "#3cc51f",
    "borderStyle": "black",
    "backgroundColor": "#ffffff",
    "list": [
      {
        "pagePath": "pages/index/index",
        "iconPath": "static/tab-home.png",
        "selectedIconPath": "static/tab-home-active.png",
        "text": "首页"
      },
      {
        "pagePath": "pages/about/about",
        "iconPath": "static/tab-about.png",
        "selectedIconPath": "static/tab-about-active.png",
        "text": "关于"
      }
    ]
  },
  "condition": {
    "current": 0,
    "list": [
      {
        "name": "首页",
        "path": "pages/index/index"
      }
    ]
  }
}

3.2 manifest.json

{
  "name": "my-app",
  "appid": "__UNI__XXXXX",
  "description": "应用描述",
  "versionName": "1.0.0",
  "versionCode": "100",
  "transformPx": false,
  "app-plus": {
    "usingComponents": true,
    "nvueStyleCompiler": "uni-app",
    "compilerVersion": 3,
    "splashscreen": {
      "alwaysShowBeforeRender": true,
      "waiting": true,
      "autoclose": true,
      "delay": 0
    }
  },
  "mp-weixin": {
    "appid": "wxXXXXXXXX",
    "setting": {
      "urlCheck": false
    },
    "usingComponents": true
  },
  "h5": {
    "router": {
      "mode": "hash",
      "base": "/"
    }
  }
}

四、页面和组件

4.1 页面结构

<template>
  <view class="container">
    <text>{{ message }}</text>
    <button @click="handleClick">点击</button>
  </view>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello UniApp'
    }
  },
  onLoad(options) {
    // 页面加载
    console.log('页面加载', options)
  },
  onShow() {
    // 页面显示
  },
  onReady() {
    // 页面初次渲染完成
  },
  methods: {
    handleClick() {
      this.message = '点击了按钮'
    }
  }
}
</script>

<style>
.container {
  padding: 20px;
}
</style>

4.2 页面生命周期

export default {
  onLoad(options) {
    // 页面加载,接收参数
  },
  onShow() {
    // 页面显示/切入前台
  },
  onReady() {
    // 页面初次渲染完成
  },
  onHide() {
    // 页面隐藏/切入后台
  },
  onUnload() {
    // 页面卸载
  },
  onPullDownRefresh() {
    // 下拉刷新
    setTimeout(() => {
      uni.stopPullDownRefresh()
    }, 1000)
  },
  onReachBottom() {
    // 上拉触底
  },
  onPageScroll(e) {
    // 页面滚动
    console.log(e.scrollTop)
  }
}

4.3 组件使用

<template>
  <view>
    <!-- 使用组件 -->
    <my-component :data="userData" @update="handleUpdate" />
  </view>
</template>

<script>
import MyComponent from '@/components/MyComponent.vue'

export default {
  components: {
    MyComponent
  },
  data() {
    return {
      userData: {
        name: 'zhangsan',
        age: 25
      }
    }
  },
  methods: {
    handleUpdate(data) {
      console.log('更新', data)
    }
  }
}
</script>

五、路由和导航

5.1 页面跳转

// 保留当前页面,跳转到应用内的某个页面
uni.navigateTo({
  url: '/pages/detail/detail?id=1&name=test',
  success: (res) => {
    console.log('成功', res)
  },
  fail: (err) => {
    console.log('失败', err)
  }
})

// 关闭当前页面,跳转到应用内的某个页面
uni.redirectTo({
  url: '/pages/detail/detail'
})

// 跳转到 tabBar 页面
uni.switchTab({
  url: '/pages/index/index'
})

// 关闭所有页面,打开到应用内的某个页面
uni.reLaunch({
  url: '/pages/index/index'
})

// 返回上一页面
uni.navigateBack({
  delta: 1 // 返回的页面数
})

5.2 页面传参

// 跳转时传参
uni.navigateTo({
  url: '/pages/detail/detail?id=1&name=test'
})

// 接收参数
export default {
  onLoad(options) {
    console.log(options.id)    // 1
    console.log(options.name)  // 'test'
  }
}

5.3 页面通信

// 方式1:通过 getCurrentPages 获取页面栈
const pages = getCurrentPages()
const prevPage = pages[pages.length - 2]
prevPage.setData({
  message: '来自详情页'
})

// 方式2:使用全局事件总线
// main.js
Vue.prototype.$eventBus = new Vue()

// 页面A
this.$eventBus.$emit('update', data)

// 页面B
this.$eventBus.$on('update', (data) => {
  console.log(data)
})

六、常用组件

6.1 基础组件

<template>
  <!-- 视图容器 -->
  <view class="container">
    <!-- 文本 -->
    <text>文本内容</text>
    <text selectable>可选文本</text>
    
    <!-- 图片 -->
    <image src="/static/logo.png" mode="aspectFit" />
    
    <!-- 按钮 -->
    <button type="primary" @click="handleClick">按钮</button>
    <button type="default" size="mini">小按钮</button>
    
    <!-- 输入框 -->
    <input v-model="inputValue" placeholder="请输入" />
    
    <!-- 文本域 -->
    <textarea v-model="textareaValue" placeholder="多行文本" />
    
    <!-- 滚动视图 -->
    <scroll-view scroll-y="true" @scrolltolower="loadMore">
      <view v-for="item in list" :key="item.id">
        {{ item.name }}
      </view>
    </scroll-view>
  </view>
</template>

6.2 表单组件

<template>
  <form @submit="handleSubmit">
    <!-- 单选框 -->
    <radio-group @change="radioChange">
      <radio value="1" :checked="radioValue === '1'">选项1</radio>
      <radio value="2" :checked="radioValue === '2'">选项2</radio>
    </radio-group>
    
    <!-- 复选框 -->
    <checkbox-group @change="checkboxChange">
      <checkbox value="A" :checked="true">选项A</checkbox>
      <checkbox value="B">选项B</checkbox>
    </checkbox-group>
    
    <!-- 开关 -->
    <switch :checked="switchValue" @change="switchChange" />
    
    <!-- 滑块 -->
    <slider :value="sliderValue" @change="sliderChange" />
    
    <!-- 选择器 -->
    <picker mode="selector" :range="pickerRange" @change="pickerChange">
      <view>当前选择:{{ pickerValue }}</view>
    </picker>
    
    <!-- 日期选择器 -->
    <picker mode="date" @change="dateChange">
      <view>选择日期:{{ dateValue }}</view>
    </picker>
    
    <button form-type="submit">提交</button>
  </form>
</template>

<script>
export default {
  data() {
    return {
      radioValue: '1',
      switchValue: false,
      sliderValue: 50,
      pickerRange: ['选项1', '选项2', '选项3'],
      pickerValue: '选项1',
      dateValue: ''
    }
  },
  methods: {
    radioChange(e) {
      this.radioValue = e.detail.value
    },
    checkboxChange(e) {
      console.log(e.detail.value)
    },
    switchChange(e) {
      this.switchValue = e.detail.value
    },
    sliderChange(e) {
      this.sliderValue = e.detail.value
    },
    pickerChange(e) {
      this.pickerValue = this.pickerRange[e.detail.value]
    },
    dateChange(e) {
      this.dateValue = e.detail.value
    },
    handleSubmit(e) {
      console.log('表单提交', e.detail.value)
    }
  }
}
</script>

6.3 媒体组件

<template>
  <!-- 视频 -->
  <video 
    src="https://example.com/video.mp4"
    controls
    @play="onPlay"
    @pause="onPause"
  />
  
  <!-- 音频 -->
  <audio 
    src="https://example.com/audio.mp3"
    controls
  />
  
  <!-- 相机 -->
  <camera 
    device-position="back"
    @error="cameraError"
  />
</template>

七、API 使用

7.1 网络请求

// uni.request
uni.request({
  url: 'https://api.example.com/data',
  method: 'GET',
  data: {
    id: 1,
    name: 'test'
  },
  header: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token'
  },
  success: (res) => {
    console.log('成功', res.data)
  },
  fail: (err) => {
    console.log('失败', err)
  }
})

// 封装请求
const request = (options) => {
  return new Promise((resolve, reject) => {
    uni.request({
      ...options,
      success: (res) => {
        if (res.statusCode === 200) {
          resolve(res.data)
        } else {
          reject(res)
        }
      },
      fail: reject
    })
  })
}

// 使用
const data = await request({
  url: '/api/data',
  method: 'GET'
})

7.2 数据存储

// 同步存储
uni.setStorageSync('key', 'value')
const value = uni.getStorageSync('key')
uni.removeStorageSync('key')
uni.clearStorageSync()

// 异步存储
uni.setStorage({
  key: 'key',
  data: 'value',
  success: () => {
    console.log('存储成功')
  }
})

uni.getStorage({
  key: 'key',
  success: (res) => {
    console.log('获取成功', res.data)
  }
})

uni.removeStorage({
  key: 'key',
  success: () => {
    console.log('删除成功')
  }
})

7.3 界面交互

// 提示框
uni.showToast({
  title: '操作成功',
  icon: 'success',
  duration: 2000
})

// 加载提示
uni.showLoading({
  title: '加载中...'
})
uni.hideLoading()

// 模态对话框
uni.showModal({
  title: '提示',
  content: '确定要删除吗?',
  success: (res) => {
    if (res.confirm) {
      console.log('确定')
    } else if (res.cancel) {
      console.log('取消')
    }
  }
})

// 操作菜单
uni.showActionSheet({
  itemList: ['选项1', '选项2', '选项3'],
  success: (res) => {
    console.log('选择了', res.tapIndex)
  }
})

7.4 设备信息

// 获取系统信息
uni.getSystemInfo({
  success: (res) => {
    console.log('屏幕宽度', res.windowWidth)
    console.log('屏幕高度', res.windowHeight)
    console.log('状态栏高度', res.statusBarHeight)
    console.log('平台', res.platform)
  }
})

// 获取网络类型
uni.getNetworkType({
  success: (res) => {
    console.log('网络类型', res.networkType)
  }
})

// 监听网络状态
uni.onNetworkStatusChange((res) => {
  console.log('网络状态', res.isConnected)
})

7.5 位置和地图

// 获取当前位置
uni.getLocation({
  type: 'gcj02',
  success: (res) => {
    console.log('纬度', res.latitude)
    console.log('经度', res.longitude)
  }
})

// 打开地图
uni.openLocation({
  latitude: 39.9,
  longitude: 116.4,
  name: '位置名称',
  address: '详细地址'
})

// 选择位置
uni.chooseLocation({
  success: (res) => {
    console.log('位置', res)
  }
})

八、条件编译

8.1 代码条件编译

// #ifdef H5
console.log('只在 H5 平台执行')
// #endif

// #ifdef MP-WEIXIN
console.log('只在微信小程序执行')
// #endif

// #ifdef APP-PLUS
console.log('只在 App 执行')
// #endif

// #ifndef H5
console.log('除了 H5 平台都执行')
// #endif

// #ifdef H5 || MP-WEIXIN
console.log('H5 或微信小程序执行')
// #endif

8.2 样式条件编译

/* #ifdef H5 */
.container {
  background: blue;
}
/* #endif */

/* #ifdef MP-WEIXIN */
.container {
  background: green;
}
/* #endif */

8.3 页面条件编译

{
  "pages": [
    // #ifdef H5
    {
      "path": "pages/h5/h5",
      "style": {}
    },
    // #endif
    
    // #ifdef MP-WEIXIN
    {
      "path": "pages/weixin/weixin",
      "style": {}
    },
    // #endif
  ]
}

8.4 静态资源条件编译

static/
├── logo.png          # 所有平台
├── logo-h5.png       # 仅 H5
├── logo-mp-weixin.png # 仅微信小程序
└── logo-app.png      # 仅 App

九、样式和布局

9.1 尺寸单位

<template>
  <!-- rpx:响应式像素,750rpx = 屏幕宽度 -->
  <view style="width: 750rpx; height: 100rpx;"></view>
  
  <!-- px:像素 -->
  <view style="width: 375px; height: 50px;"></view>
  
  <!-- 百分比 -->
  <view style="width: 50%;"></view>
</template>

9.2 Flex 布局

<template>
  <view class="container">
    <view class="item">1</view>
    <view class="item">2</view>
    <view class="item">3</view>
  </view>
</template>

<style>
.container {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  align-items: center;
}

.item {
  flex: 1;
}
</style>

9.3 全局样式

// uni.scss
$uni-color-primary: #007AFF;
$uni-color-success: #4CD964;
$uni-color-warning: #F0AD4E;
$uni-color-error: #DD524D;

// 使用
.text-primary {
  color: $uni-color-primary;
}

十、插件和扩展

10.1 uni-ui 组件库

# 安装
npm install @dcloudio/uni-ui

# 使用
<template>
  <uni-icons type="contact" size="30" />
  <uni-badge text="99" type="error" />
  <uni-card title="标题" note="备注">
    <text>内容</text>
  </uni-card>
</template>

10.2 第三方插件

# 安装插件
npm install vuex-persistedstate

# 使用
import createPersistedState from 'vuex-persistedstate'

const store = new Vuex.Store({
  plugins: [createPersistedState()]
})

十一、性能优化

11.1 图片优化

<template>
  <!-- 使用 webp 格式 -->
  <image src="/static/image.webp" />
  
  <!-- 懒加载 -->
  <image 
    src="/static/image.jpg" 
    lazy-load
    mode="aspectFit"
  />
  
  <!-- 使用网络图片时压缩 -->
  <image 
    :src="imageUrl" 
    mode="aspectFit"
    @error="handleImageError"
  />
</template>

11.2 列表优化

<template>
  <!-- 使用 key -->
  <view v-for="item in list" :key="item.id">
    {{ item.name }}
  </view>
  
  <!-- 虚拟列表(大数据) -->
  <recycle-list 
    :list="largeList"
    template-key="type"
  />
</template>

11.3 代码分包

{
  "subPackages": [
    {
      "root": "pages/sub1",
      "pages": [
        {
          "path": "index/index",
          "style": {}
        }
      ]
    }
  ],
  "preloadRule": {
    "pages/index/index": {
      "network": "all",
      "packages": ["pages/sub1"]
    }
  }
}

十二、常见问题

12.1 平台差异

// 处理平台差异
// #ifdef H5
// H5 特定代码
// #endif

// #ifdef MP-WEIXIN
// 微信小程序特定代码
// #endif

// #ifdef APP-PLUS
// App 特定代码
// #endif

12.2 样式兼容

/* 使用条件编译 */
/* #ifdef H5 */
.container {
  /* H5 样式 */
}
/* #endif */

/* #ifdef MP-WEIXIN */
.container {
  /* 小程序样式 */
}
/* #endif */

12.3 调试技巧

// 开发环境调试
// #ifdef H5
console.log('H5 调试')
// #endif

// #ifdef MP-WEIXIN
console.log('小程序调试')
// #endif

// 使用 uni.showToast 调试
uni.showToast({
  title: JSON.stringify(data),
  icon: 'none',
  duration: 3000
})

十三、发布和打包

13.1 H5 发布

# 构建 H5
npm run build:h5

# 部署到服务器
# 将 dist/build/h5 目录上传到服务器

13.2 小程序发布

# 构建微信小程序
npm run build:mp-weixin

# 使用微信开发者工具打开 dist/build/mp-weixin
# 上传代码,提交审核

13.3 App 打包

# 构建 App
npm run build:app-plus

# 使用 HBuilderX
# 1. 发行 → 原生 App-云打包
# 2. 或使用本地打包

十四、最佳实践

14.1 项目结构

project/
├── pages/              # 页面
├── components/         # 组件
├── static/            # 静态资源
├── utils/             # 工具函数
├── api/               # API 接口
├── store/             # 状态管理
├── common/            # 公共文件
└── uni_modules/       # uni-app 插件

14.2 代码规范

// ✅ 使用 async/await
async function fetchData() {
  try {
    const res = await uni.request({
      url: '/api/data'
    })
    return res.data
  } catch (error) {
    console.error(error)
  }
}

// ✅ 统一错误处理
const handleError = (error) => {
  uni.showToast({
    title: error.message || '操作失败',
    icon: 'none'
  })
}

14.3 性能优化

// ✅ 使用防抖节流
import { debounce, throttle } from '@/utils/index'

// 防抖
const handleSearch = debounce((keyword) => {
  // 搜索逻辑
}, 500)

// 节流
const handleScroll = throttle(() => {
  // 滚动逻辑
}, 200)

十五、推荐资源

官方文档:

学习资源:


十六、总结

UniApp 核心要点:

一套代码 + 多端运行 + 条件编译 + 丰富组件 = 高效跨平台开发

核心心法:

UniApp 让跨平台开发变得简单。 掌握条件编译是处理平台差异的关键。


📝 文档信息

  • 作者: 阿鑫
  • 更新日期: 2026.1